UDN
Search public documentation:

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

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 Home > UnrealScript Foundation Concepts

UnrealScript Foundation Concepts


Overview


The UnrealScript language is a custom built scripting language used solely by the Unreal Engine to add new gameplay items to games made with the Unreal Engine without requiring the entire engine be recompiled. It provides gameplay programmers with features specifically designed for use in games and makes creating event-based gameplay much easier and more efficient to implement.

The topics discussed here may contain mentions of language features, such as classes, variables, functions, etc., that are not themselves covered within the scope of this document. This page is meant more to cover certain concepts that do not necessarily deal with a specific language feature but are extremely important to understand, especially when starting out. Please see the UnrealScript Reference for complete details of all language features.

What is an UnrealScript?


Each UnrealScript file is also often referred to an UnrealScript. This is a simple text file with a .uc file extension that contains the definition of a single UnrealScript Class. See UnrealScript Classes for more information on what a class is and how they are used by the engine as well as how to define new classes.

UnrealScripts can be created with virtually any text editor program, though some text editors do provide highlighters and other special functionality specific to UnrealScript that make working with them much easier. Certain IDEs - nFringe and WOTGreal - also provide a complete integrated solution for developing Unreal projects using UnrealScript.

Script Naming and Location


The name of the UnrealScript file must always match exactly the name of the class it defines. If there is a discrepancy between the file name and class name, an error will occur during the compiling process.

UnrealScripts reside in various folders inside the Development\Src directory. This directory contains many folders by default - Core, Engine, UDKBase, UnrealEd, etc. - each representing a different UnrealScript project, or package. Inside each of those package folders is a Classes folder that contains all the UnrealScripts belonging to that package.

When you want to add your own custom UnrealScripts to be used by the engine for your game, you must create one or more new package folders and subsequent Classes= folders in the ==Development\Src directory. In these is where you will place your UnrealScripts.

Please see Custom UnrealScript Projects for more information on setting up your own custom UnrealScript packages to be used by the engine.

Anatomy of an UnrealScript


MyActor.uc

/*********************
 * Class Declaration *
 *********************/


class MyActor extends Actor;


/************************************
 * Instance Variables/Structs/Enums *
 ************************************/


enum MyEnum
{
	ME_None.
	ME_Some,
	ME_All
}

struct MyStruct
{
	var int IntVal;
	var float FloatVal;
}

var int IntVar;

var float FloatVar;

var bool BoolVar;

var Actor ActorVar;

var MyEnum EnumVar;

var MyStruct StructVar;


/**********************
 * Functions & States *
 **********************/


function MyFunction()
{
	local int TempInt;

	if(ActorVar != none && BoolVar)
	{
		TempInt = IntVar;
	}
}

state NewState
{
	function MyFunction()
	{
		local float TempFloat;
	
		if(ActorVar != none && BoolVar)
		{
			TempFloat = FloatVar;
		}
	}
}


/**********************
 * Default Properties *
 **********************/


defaultproperties
{
	IntVar=5
	FloatVar=10.0
	BoolVar=true
}

Class Declaration
Every UnrealScript begins with a class declaration. This says what the class associated with this script is named, what other class it inherits from, and allows several other aspects to be controlled through class specifiers, which are options added onto the end of the declaration. This must be the first thing in the script other than any comments you might wish to add at the beginning describing the class, specifying copyrights, etc.
Instance Variables/Structs/Enums
Instance variable declarations follow the class declaration. This tells the class what properties it contains.
Functions & States
Function and state declarations make up the majority of the class. These specify what actions the class can perform.
Default Properties
The defaultproperties block always is the last thing in the script. It provides a place to specify default values for any of the instance variables declared in the class.

Scripts and Classes vs Objects and Actors


A class is essentially a blueprint for an item to be used by the engine in the game. It defines the properties and behaviors the item can have, via variables and functions. These blueprints are used to create individual, unique instances of these items. A unique instance of a class is called an object in general object-oriented programming. This is reinforced in UnrealScript because Object is also the base class in the hierarchy. So every class inherits from Object at some point and, therefore, any instance of any class is technically an object. An object has its own property values that can be modified or accessed and behaviors or actions that can be executed without affecting any other flak cannon instance in the world.

Ok, so a class is a blueprint and an object is an instance, but what does this really mean? Take the Flak Cannon, one of the weapons commonly found in Unreal Tournament, as an example. A flak cannon has ammo, fires several small projectiles as its primary fire, and fires a large shell as its alt fire. That is the (overly simplified) blueprint of a flak cannon, or the Flak Cannon class. Now, when a player runs over a pickup for a flak cannon in the world, that player is given an instance of a flak cannon. What does this really mean though? It means the Flak Cannon class is used as a blueprint to create a new flak cannon. This is a particular flak cannon with its own property values that can execute behaviors separate from any other flak cannon. So, when the player fires, the ammo count of that particular flak cannon instance is reduced and a projectile is fired from that particular flak cannon and no other.

This is the fundamental concept of object-oriented programming and something you must understand before delving into UnrealScript programming. With that said, we won't get into much more detail about it here as there are plenty of resources covering the topic already that go into great detail.

Communication Between Scripts


So, if you create classes in UnrealScript, but the world is populated with instances and not the classes themselves, how does one script communicate with another? The classes don't know what instances will exist in the world when you write them. An example of this would be the same player with a flak cannon from above. How does the player tell that particular flak cannon to fire and not some other flak cannon? The answer is references. One of the types of variables in UnrealScript is the Object reference data type. This is a fancy name for a variable whose type is an Object (or Actor). The value of an Object reference variable is a reference to an instance of that type of object. For instance, the Controller class has a variable it uses to reference the Pawn it is currently controlling. This variable's type is Pawn (and its name is coincidentally Pawn too). The declaration for this variable looks like:

var Pawn Pawn;

In the flak cannon example above, the class for the player (actually the Pawn in this case) would have a variable that can hold a reference to the weapon the player is currently using. This variable points to the instance of the flak cannon that was given to the player when they ran over the pickup for that weapon. This allows a means of communicating directly to that instance.

This is a very important concept that is central to practically everything you will do in UnrealScript. Whenever one object needs to access a value of a property of another object or tell another object to perform an action, that object needs a reference to the other. Of course, now you should be wondering how you get a reference to the instance in the world that you want to communicate with. It's not enough to simply declare an Object reference variable for the reference. An Object reference variable is more or less just a container to hold a reference. It's value is none until you assign something to it. You actually have to populate that variable with a value - the instance you want to reference. Unfortunately, there isn't a steadfast rule to follow here. There are many ways to obtain a reference to an instance of an object in the world and how you do so depends entirely on the particular situation and relationship between the two objects. Some examples are:

  • Helper functions - In some cases, helper functions are provided in certain classes that return a reference to a commonly used object. For example, the GFxMoviePlayer class has a GetPC() function that returns a reference to the PlayerController that owns the movie.
  • Events - One actor causes an event to be fired in another, such as a Touch event. In many cases, the actor that triggers an event has a reference passed to that event automatically by the engine. This allows you to use that reference inside the event or saved it to a variable to be used later.
  • Spawning - The Spawn() function returns a reference to the actor it creates. When one actor spawns another and needs to be able to communicate with the spawned actor immediately or later on, you can use the reference returned by the Spawn() function or save the reference to a variable.
  • 3rd Party - Quite often the object you need a reference to is already being referenced in another class to which you do have a reference to. This means you can borrow that reference to use for your own purposes. An example of this that is commonly needed and used is the current gametype, or instance of the GameInfo class. The only class that has a direct reference to the gametype is WorldInfo by way of its Game variable, but you often need to access it in other locations. Fortunately, every Actor has a reference to the current WorldInfo instance via the WorldInfo variable. This means you can use your WorldInfo reference to get access to the reference to the GameInfo instance. This method is often overlooked by those just starting out. Always check the objects you do have references to as they may have references to what you are looking for.
  • Iteration - Using iterator functions is kind of like performing a search. They allow you to specify some criteria and return a list of results in the form of references to objects. You can act on these directly in the iterator or save them to a variable for use later.
  • Editor - In some cases, you may leave it up to the level designer to set a reference to an actor in the editor. All this requires is creating an editable variable to hold the reference.

Once you have an object reference, you have access to the variables and functions belonging to that object (unless they are specified as protected/private) through the use of a special syntax often called dot notation. This just means that the object reference is followed by a dot - or period - and then the name of the variable or function you wish to access or execute.

Take the Pawn reference in the Controller as mentioned above. If you want to access the Health variable in the Controller's Pawn, you can do so like this:

Pawn.Health

Similarly, if you want to tell the Pawn to start firing its main weapon, you could do so using the StartFire() function:

Pawn.StartFire(0);

Object reference variables hold references to objects, but you do not always need an Object reference variable in order to reference an object. While you can assign the result of a helper function to a variable and use that as your reference, it is also possible to just use the result of the function directly.

To demonstrate this, let's use the GetPC() function in the GFxMoviePlayer class discussed above. This returns the PlayerController that owns the Scaleform movie. Now let's say we want to access the health of the current player, which is held in the Pawn controlled by the PlayerController. Like above, we can use the Pawn variable in the controller, but this time we have to reference the controller first since we are in the GFxMoviePlayer class. We don't need to save this reference to a variable though. We can simply use the dot notation directly with the GetPC() function call.

GetPC().Pawn.Health

Now, let's assume there is a button on the UI that is used to fire the player's weapon. We can do this just like we did previously.

GetPC().Pawn.StartFire(0);

This works because GetPC() can essentially be thought of as a reference to a PlayerController instance since that is what its return value is as you can see from the declaration of the function in the GFxMoviePlayer class:

/**
 * Helper function to get the owning player controller for this movie
 *
 * @return The PlayerController corresponding to the LocalPlayerOwnerIndex that owns this movie
 */
event PlayerController GetPC()
{
	local LocalPlayer LocalPlayerOwner;
	
	LocalPlayerOwner = GetLP();
	if (LocalPlayerOwner == none)
	{
		return none;
	}
	return LocalPlayerOwner.Actor;
}

How Do You Use Existing Scripts?


The Unreal Engine contains a large amount of UnrealScript classes already implemented in several different packages located in the Development\Src directory. It is important to understand what these classes are and how they can be used in your own game. The existing classes mainly provide basic and generic functionality. They are essentially part of the engine itself and not part of "the game", though this can be a fuzzy distinction at times. Many of the main systems and the classes that comprise them are explained in various "technical guides" here on the UDN. To gain a more complete understanding of all of the classes available, you may need to do some research by digging through the actual scripts themselves and analyzing the code, reading the comments, etc. An extremely useful tool in this regard is UnCodeX. It creates Javadocs-style documentation from the scripts that is easily navigable.

The first thing to understand is that you should not modify the existing scripts under normal circumstances. Modifying a native class or a class in a native package without recompiling the engine can have serious consequences, generally resulting in the engine crashing when the game is run.

UnrealScript is object-oriented which means that each class inherits - or extends in UnrealScript parlance - from another class creating a parent-child relationship. The child class inherits all of the variables, functions, states, etc. that exist in the parent class. So instead of modifying existing scripts, you should extend from them using them as starting points for your own custom classes. The beauty of this is that you are not limited to what the parent class passes down to the child class as you can add any new variables and functions you want; nor are you limited by how the parent class implemented the functions it passed down since UnrealScript allows you to override any inherited function to make it perform the exact actions you desire.

In some cases, only extending from classes and never modifying them may seem limiting as it is a common thought to add a variable to an existing base class so that it is available to all the children of that class. There is usually an alternative way to go about designing your systems that does not require modifying an existing class.

Bottom line: extend from existing classes and override functionality; never modify them.

What Class To Extend From


The typical approach to class design in UnrealScript is to make a new class (for example a Minotaur monster) which extends an existing class that has most of the functionality you need (for example the Pawn class, the base class of all monsters). With this approach, you never need to reinvent the wheel -- you can simply add the new functionality you want to customize, while keeping all of the existing functionality you don't need to customize. This approach is especially powerful for implementing AI in Unreal, where the built-in AI system provides a tremendous amount of base functionality which you can use as building blocks for your custom creatures.

Knowing how far up the hierarchy to extend from is extremely dependent on the situation. Many times it comes down to a choice between reimplementing some functionality to avoid overriding a ton of unnecessary stuff. Which path is better is completely up to you. The best guidance is, again, to choose the class that is most similar or shares the most commonalities with the class you want to implement, and then go from there.

Object Hierarchy


Before beginning work with UnrealScript, it's important to understand the high-level relationships of objects within Unreal. The architecture of Unreal is a major departure from that of most other games: Unreal is purely object-oriented (much like COM/ActiveX), in that it has a well-defined object model with support for high-level object oriented concepts such as the object graph, serialization, object lifetime, and polymorphism. Historically, most games have been designed monolithically, with their major functionality hard-coded and unexpandable at the object level, though many games, such as Doom and Quake, have proven to be very expandable at the content level. There is a major benefit to Unreal's form of object-orientation: major new functionality and object types can be added to Unreal at runtime, and this extension can take the form of subclassing, rather than (for example) by modifying a bunch of existing code. This form of extensibility is extremely powerful, as it encourages the Unreal community to create Unreal enhancements that all interoperate.

Object
The parent class of all objects in Unreal. All of the functions in the Object class are accessible everywhere, because everything derives from Object. Object is an abstract base class, in that it doesn't do anything useful. All functionality is provided by subclasses, such as Texture (a texture map), TextBuffer (a chunk of text), and Class (which describes the class of other objects).
Actor (extends Object)
The parent class of all standalone game objects in Unreal. The Actor class contains all of the functionality needed for an actor to move around, interact with other actors, affect the environment, and do other useful game-related things.
Pawn (extends Actor)
The parent class of all creatures and players in Unreal which are capable of high-level AI and player controls.
Class (extends Object)
A special kind of object which describes a class of object. This may seem confusing at first: a class is an object, and a class describes certain objects. But, the concept is sound, and there are many cases where you will deal with Class objects. For example, when you spawn a new actor in UnrealScript, you can specify the new actor's class with a Class object.

With UnrealScript, you can write code for any Object class, but 99% of the time, you will be writing code for a class derived from Actor. Most of the useful UnrealScript functionality is game-related and deals with actors.

UnrealScript Programming Strategy


Here are some tips on how to write UnrealScript code effectively, and take advantage of UnrealScript's strengths while avoiding the pitfalls.

  • UnrealScript is a slow language compared to C/C++. A typical C++ program runs about 20X faster than UnrealScript. The programming philosophy behind all of our own script writing is this: Write scripts that are almost always idle. In other words, use UnrealScript only to handle the "interesting" events that you want to customize, not the rote tasks, like basic movement, which Unreal's physics code can handle for you. For example, when writing a projectile script, you typically write a HitWall(), Bounce(), and Touch() function describing what to do when key events happen. Thus 95% of the time, your projectile script isn't executing any code, and is just waiting for the physics code to notify it of an event. This is inherently very efficient. In our typical level, even though UnrealScript is comparably much slower than C++, UnrealScript execution time averages 5-10% of CPU time.
  • Exploit latent functions (like FinishAnim and Sleep) as much as possible. By basing the flow of your script execution on them, you are creating animation-driven or time-driven code, which is fairly efficient in UnrealScript.
  • Keep an eye on the Unreal log while you're testing your scripts. The UnrealScript runtime often generates useful warnings in the log that notify you of nonfatal problems that are occurring.
  • Be wary of code that can cause infinite recursion. For example, the "Move" command moves the actor and calls your Bump() function if you hit something. Therefore, if you use a Move command within a Bump function, you run the risk of recursing forever. Be careful. Infinite recursion and infinite looping are the two error conditions which UnrealScript doesn't handle gracefully.
  • Spawning and destroying actors are fairly expensive operations on the server side, and are even more expensive in network games, because spawns and destroys take up network bandwidth. Use them reasonably, and regard actors as "heavy weight" objects. For example, do not try to create a particle system by spawning 100 unique actors and sending them off on different trajectories using the physics code. That will be sloooow.
  • Exploit UnrealScript's object-oriented capabilities as much as possible. Creating new functionality by overriding existing functions and states leads to clean code that is easy to modify and easy to integrate with other peoples' work. Avoid using traditional C techniques, like doing a switch() statement based on the class of an actor or the state, because code like this tends to break as you add new classes and modify things.
  • UnrealScript .u packages are compiled strictly in the order specified by the .ini file's EditPackages list, so each package can only reference other objects in itself and in previously-compiled packages, and never in subsequently-compiled packages. If you find that a need for circular references between packages arises, the two solutions are:
    1. Factor the classes into a set of base classes compiled in the first .u package, and a set of child classes compiled in the second .u package, making sure that the base classes never reference the child classes. This is a good programming practice anyway, and it usually works.
      Note: If a given class C needs to reference a class or object O in a later-compiled package, you can often factor that class into two parts: an abstract base class definition C that defines a variable MyO in the first package (but doesn't contain a default value for MyO in its default properties), and a subclass D in the second package that specifies the proper default value for MyO which can only be done from within that second package.
    2. If the two .u packages are inextricably intertwined by references, then merge them into a single package. This is reasonable because packages are intended as a unit of code modularity, and there's not a real benefit (such as memory savings) to separating these inseparable sets of classes into multiple packages.