UDN
Search public documentation:
UT3Mods
日本語訳
中国翻译
한국어
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
中国翻译
한국어
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
Mod Authoring for Unreal Tournament 3
Introduction
Developing mods for the Unreal engine can be an extremely rewarding task. In the current state of the games industry, there is little better a way for a skilled programmer or aspiring artist to show the world what he or she is capable of. This document is intended to give interested mod authors the information they need to successfully create modifications for Unreal Engine games. It will include technical information as well as pointers on mod development. If you work hard, you can get some fun stuff done through mod authoring.Some Advice to Start With
When you begin developing a mod you should start small. Don't plan to write a Total Conversion (TC) from the very start. If you set goals that are too hard to reach, you'll get frustrated in the process of working towards them; and you may never be able to bring the vision to life. It is much better to set a series of small goals and work to each one in turn. Start with a simple idea that could be expanded into a larger game. Always work on small, managable chunks that could each be released in their own right. If you do undertake a large project, organize your features into a release schedule. If your game is going to have 5 new weapons, make a release with 2 or 3 while your work on the others. Pace yourself and think about the long term. Everyone has ideas. Everybody thinks they have a revolutionary new game concept that no one else has ever thought of. Having cool ideas will rarely get you anywhere in the games industry. You have to be able to implement your ideas or provide some useful skill. This also applies to mod authoring. If you become a skilled or notable mod author, you will find people propositioning you to implement their ideas. Never join a project whose idea man or leader has no obvious development skills. Never join a project that only has a web designer. You have your own ideas. Focus on them carefully and in small chunks and you will be able to develop cool projects. Remember that developing a mod doesn't mean much if you never release it. Scale your task list so that you can release something quickly, adding and improving features as your mod matures. If you hold back your mod until everything is perfect, you'll find yourself never releasing anything. Once you have your idea, you need to choose what kind of mod type is right for you. After that you can set everything up and begin making your visions a reality.Setting up a Mod Project
It's time to get your hands dirty. First, you should understand how an Unreal Engine game is typically installed. After that, you should know where your mod fits in with game, and some of the ways the Unreal Engine treats content - both original and new. Then, depending on your approach, you can start to set up everything and begin creating your mod.Directory Structure
By default, Unreal Tournament 3 will be installed to the following directory:<InstallDrive>:\Program Files\Unreal Tournament 3Additionally, user settings will be stored for each user:
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\
Running and Testing
The game can be run by clicking the desktop icon, from the Start Menu, or via the following command:UTGameThe game editor can be run from the Start Menu, or via the following command:
UTGame editor
Useful Console Commands
help - gives information about a given command. Useful parameters includelist
.
UTGame help [command]run - runs a commandlet. If you have a commandlet named TestCommandlet, you can use just
Test
and the command will append Commandlet
.
UTGame run [commandlet name]
Packages
Now its time to set up Unreal Tournament to build your project. First things first, you need to understand how UnrealScript uses packages. Packages are collections of game resources. The resources can be any of several types of assets: textures, models, animations sounds, music, UI scenes. The package format is the same for all resources and multiple resource types can be mixed in a package. For the sake of sanity, Unreal Tournament splits up packages into resources. The textures directory contains packages with textures, the sounds directory contains packages with sounds and so forth. Even though these packages may contain different types of content, they have have the same file extention (.upk) and they are still the same kind of file. Programmers will be working with UnrealScript and dealing with compiled game code package (.u) files. Code packages primarily contain compiled UnrealScript, but may also contain textures and sounds that the code depends on. See the Unreal Packages page for more information.Programming
When getting started you'll need to do a lot of research on your own. You'll only become a solid UnrealScript programmer if you spend time to acquaint yourself with the framework and the code that is available to you. Learn creative ways to solve problems related to your mod design goals and empower yourself to maximize the tools that you have available.UnrealScript
UnrealScript is an object-oriented (OO) language. If you aren't familiar with OO, now is a good time to take a detour and read a guide to object oriented programming: http://www.orangesmoothie.org/tuts/GM-OOtutorial.html. This document is fairly old, but still a good resource. Since UnrealScript is object-oriented, you won't be editing any of the original source. This may be different from other game engines. In Unreal, you will subclass the classes that shipped with Unreal Tournament, overriding and extending them to suit your needs. For a complete reference on UnrealScript, see the UnrealScript Reference page.UnrealScript Source Files
You can download the latest UnrealScript Source Files here: http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.5.rar Previous versions:- http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.4.rar
- http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.3.rar
- http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.2.rar
- http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.1.rar
- http://udn.epicgames.com/Files/UT3/Mods/UT3ScriptSource_1.0.rar
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Src\YourModName\ClassesNOTE: The Src\YourModName\Classes directory structure may need to be created. Before compiling UnrealScript source, you must first register your script mod name with the engine by opening UTEditor.ini and adding the following line under the
[ModPackages]
section:
ModPackages=YourModNameThe game's UnrealScript compiler can be run via the following command:
UTGame make... or for a full rebuild:
UTGame make -fullNOTE: Never modify the core UT UnrealScript classes! Compiled UnrealScript packages will be placed in the following location:
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Unpublished\CookedPC\Script\YourModName.uNOTE: The Script directory may need to be created. A configuration file will be automatically created for the mod package and placed in the following location:
<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Config\UTYourModName.iniNote that the configuration file autogeneration only detects subclasses of UT* classes as those are the only ones that have a bExportMenuData flag to control it. If your mod does not begin with UT prefixed, it will be done for you. If your mod does not have any subclasses of UT* classes, you will have to manually create a configuration file. When you open the editor, any mod packages in the UTEditor.ini
ModPackages
list will automatically be loaded at editor startup, and so any placeable Actor classes will automatically appear in the Actor Browser. To suppress this behavior, run the editor with the =-nomodautoload flag.
Remember that you'll probably want to spend a lot of time just pooring over the massive amount of UnrealScript source code for the game. Trace execution paths and look at how the various classes override and interact with each other. It can be very intimidating at first, but with time you'll get more experienced with where things are and what things do. Don't be afraid to go online and ask questions in community mailing lists and forums, either. If you spend the time it takes to learn, you will be rewarded with the ability to take on larger, more difficult projects.
Content Creation
Levels
When using the game editor, levels will be saved to the following directory:<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\Unpublished\CookedPC\CustomMapsFor your level to be visible in the game menus, it needs to be saved in a subdirectory of CustomMaps. Anything saved directly in the CustomMaps directory itself will be ignored. So, you’ll want create a directory that is appropriate to your mod name. Remember when saving a level to do so with the right gametype prefix (e.g. DM-
- Tell the game to run out of the Unpublished directory by running with =UT3.exe –useunpublished. This is best for fast turnaround time while developing.
- Use the Publish button in the editor; this copies the contents of Unpublished to Published so the game will auto-detect it.
Publishing and Cooking
The CookedPC naming convention may seem confusing but it’s a relatively simple process once everything is set up properly. Cooking bakes out unnecessary content from the map file, creates the shader cache information needed to properly load and run your map, and improves performance and loading times. Publishing sets up the files for distribution and puts everything into the proper directory so that other people can play what you’ve created. Regarding the Published and Unpublished behavior: content that is saved in the game editor is written to the Unpublished directory by default. This is the working directory for mod authors, and can be found here:<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\ <InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\Script\ <InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\CustomMaps\ <InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\CustomChars\ <InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\Localization\<LANGUAGE>\The Published directory is located here:
…\Documents and Settings\<UserID>\My Documents\My Games\Unreal Tournament 3\UTGame\Published\CookedPC\Saving a game in the editor should create the UnPublished folder. Publishing a file should create the Published folder. If either of those directories don’t exist, you’ll need to create them manually. When you download a map and want to play it, put it in the Published folder and it will then be recognized by the game and appear in the in menus. Note that files that are not in the proper directory will not be recognized by the game and will not load properly or appear in the menus. The game naturally runs content from the Published directory, unless the
-useunpublished
flag is specified when running up the game. The typical mod workflow will be to iterate development on script and maps and run the game with -useunpublished
. When the mod is ready, a final Publish will write cooked content to the Published directory, and these loading-optimised files will be used for distribution. For Scripts and localized text files, they will have to be copied over manually.
Alternatively, mod authors can run their in-development content without the -useunpublished
flag by issuing Publish when content changes are made. In this way, the mod content will be copied uncooked over to the Unpublished directory, where the game will automatically recognize it. As above, it is recommended that a final Publish is done before distributing a mod.
To summarize, the -useunpublished
flag is a mod developer shortcut for not having to continually copy files while working.
Localization
A localized text file for your mod package should be created and placed in the following directory:<InstallDrive>:\Documents and Settings\<userID>\My Documents\My Games\Unreal Tournament 3\UTGame\UnPublished\CookedPC\Localization\<LANGUAGE>\YourModName.int
Distribution
Previously, mods were packaged up using the Unreal Engine'smaster
commandlet to create mod installer files (.umod for UT and .ut2mod for UT2003 and UT2004). Now mods will simply packaged into a compressed archive using a utility such as WinRAR.
Naming Conventions
For consistency and maintainability, it's very wise to establish some naming conventions and other best practices such as coding standards early on in the development of your project. This will keep you and any team members on the same page. For programming, we recommend the following naming conventions:- UTMutator_
- for UT3 Mutator classes - UTGame_
- for UT3 GameType classes - UTGameRules_
- for UT3 GameRules classes (used by your Mutator or GameType) - UTMod_
- for other UT3 mods; also good for the package and associated directory
Consoles
PlayStation 3 Mod Support
Please see the PS3 Mods page for more information on making user-created content for the PlayStation 3 console.The Four Mod Types
Mutators
Mutators are mini-mods. They have limited functionality as defined by the Mutator class. Mutators should follow certain rules - rules that are established by the GameType. If these rules can't be followed or are too limiting, you should probably work on a GameType mod. The first rule is that Mutators should be able to work with any other Mutator - especially the ones that come with the game. If you write a "Vampire" Mutator that allows the player to drain life from an enemy he shoots, the Mutator should work well if combined with one of the Arena mutators or the No Powerups Mutator. This is one of the beneficial features of the Mutator system. They slightly change (or mutate) gameplay, allowing for interesting combinations. The second rule is that Mutators should only change gameplay in a slight fashion. Although that's a vague way of putting it, you need to try and restrict your Mutator behavior. Careful Mutator design will increase the chances of your Mutator working with other mods and will decrease your support effort. The third rule is that Mutators should share resources with other Mutators. If your Mutator implements theModifyPlayer
function, you need to call NextMutator.ModifyPlayer
somewhere inside your version of the function. This ensures that any Mutator on the Mutator list after your mod gets a chance to deal with the function call. Failing to do this is poor programming style and not community-friendly.
GameTypes
GameTypes are a much larger class of mod. They do everything the Mutator can't and allow you access to a much larger range of functionality. If your idea can't be implemented within a Mutator, you should work on a GameType. The drawback of a GameType is that it cannot be mixed with other GameTypes. For example, Capture the Flag is a GameType in Unreal Tournament. It is a wholly different style of gameplay from Deathmatch (another GameType). GameTypes are implemented as subclasses of the GameInfo class, specific to the game. There aren't any specific rules for GameTypes, other than some client-server issues that you should be aware of (and that we will discuss later).Total Conversions
Total Conversions (TC) completely bypass the established game classes and start at the engine core. Rather than use game-specific subclasses, TC's are implemented as subclasses of the base engine classes, such as the GameInfo class. If your mod doesn't exist in the realm of the game's universe, or if you want to try something completely different, then a TC is the way to go. Just be aware that you will be doing everything from scratch.Custom Content
Depending on the GameType, some mods may require many new elements such as AI, or special Actors that are separate from the game rules themselves. It is possible also to create mods that don't change gameplay through the GameInfo or Mutator classes. These would include Player Plugin Models (PPM), new weapons, or new vehicles. Of course, Levels are a great way to create new content without writing any code. You can simply carve out a new level and decorate it using existing content assets that came with the game; or you can create your own textures and models (static meshes), as well! For more guidance on designing a multiplayer level, see the Multiplayer Map Theory (UT3) page.How to Make a Mutator
Mutators are a great place to cut your teeth on UnrealScript because you are exposed to a limited, but powerful subset of the engine. As I previosly mentioned, Mutators should only modify the game code in a relatively slight way. This increases the chances your mutator will work well when mixed with other mutators. (For example, you can play FatBoy, InstaGib, No Powerups deathmatch. A mix of 3 mutators). Lets look at a very simple Mutator mod:class ExampleMutator extends Mutator; defaultproperties { }
Configuration
By default, a configuration file will be made for your package, with references to your mutator. The following information is relevant:FriendlyName=Example Mutator Description=An Example MutatorSome configuration changes must be made in order to get the new mutator to properly show up in the menus. The Instant Action and Multiplayer menus look in all configuration files for classes that are subclasses of UT classes. If you add these lines to your package's localized text file, you'll get an entry called "Example Mutator" in the list of mutators. You will need to provide text in your mod package localized text file, as follows:
;/////////////// ; Mutator Names ;/////////////// [ExampleMutator UIDataProvider_Mutator] FriendlyName=Example Mutator Description=An Example Mutator.Some mods can be configured in the game menus, as well. To do so, you have to create a UI Scene and save it in a package, and reference the package via the
UIConfig
member variable. This can be done in the defaultproperties
of the Mutator class.
Here is an example:
defaultproperties { UIConfigScene=UIScenes_Example.Menus.ExampleMutatorConfig }With a simple Mutator we can take a look at a few of the methods available in Mutator.
The Anatomy of Mutator - a First Look
So now you've had your first exposure to writing a simple UT mod. Clearly this isn't enough to shake the world or get a job in the industry. It's worth taking a closer look at the methods inside the Mutator base class. This will give you a better idea of what you can do with them. It only scratches the surface, however, because you have the power to use a multitude of inherited functions as well as interact with other objects.Mutate
lets your mutator define new commands that player's can bind to keys. If a player binds mutate givehealth
to a key and then uses that key, every mutator will get a mutate call with a givehealth
parameter. Your mutator might look for this string and give the player who sent the message some extra health.
ModifyLogin
ModifyPlayer
is called by the game whenever a Pawn is respawned. It gives you a chance to modify the Pawn's variables or perform some game logic on the Pawn. Remember to call Super.ModifyPlayer()
if you override this function. That will call the parent class' version of the function.
AlwaysKeep
allows you to interdict objects that the game wants to add to the world. You can replace objects on the fly with other objects as they appear. The Unreal Tournament Arena mutators are a great example of this. They take all the weapons in a game and replace them with one other weapon. If you are adding a new weapon to the game, you might want to add an Arena mutator for it.
IsRelevant
is called when the game wants to add an object to the world. You can override it with special code and return true, to keep the object, or false, to reject it. If you say false, the object will be destroyed.
CheckRelevance
CheckReplacement
Taking It Further: Game Rules
At the core, the Mutator class traps certain events that can be modified for tweaking. Of course, some limitations can be experienced right away, such as scoring kills or affecting damage. This is where Game Rules come in. Each GameInfo game subclass has a list of Game Rules called GameRulesModifiers. You can create a subclass of GameRules that can tweak the way certain game events are handled.FindPlayerStart
HandleRestartGame
CheckEndGame
OverridePickupQuery
PreventDeath
ScoreObjective
ScoreKill
NetDamage
Looking even futher, your mutator could even draw information to the HUD. While there is more flexibility for doing this from a GameType, the limited functionality should be enough for a Mutator. Simply create a replicated Actor subclass that adds itself to the PostRenderedActors
array in the UTHud class.
With this information, you should be able to make a lot of changes with very few classes. The best way to learn how to maximize the power of mutators is to read the code in the mutators that ship with UT.
Introduction to GameTypes
Mutators can do some cool stuff. They are pretty easy to understand and they can do a lot of things by interacting with certain game events and rules. They can be mixed and matched to get even cooler effects; but they are NOT very powerful. If you want to make a new type of game (such as a Jailbreak or Rocket Arena style mod), you can't do it with mutators. You need to have complete control over the game rules. That's where the GameInfo series of classes come into play. GameInfo is a class located in Engine. It is created by the game engine and is the core of the game play rules. Unreal Tournament makes use of a series of GameInfo subclasses located in the UTGame package. The UTGame class contains code that is universal to all of Unreal Tournament's game types. UTDeathmatch contains the code for running a normal deathmatch. UTTeamGame contains code for team deathmatch as well as general team management code. UTCTFGame, which is a subclass of UTTeamGame, implements that particular gametype. The first step in writing your new game type is to determine which GameInfo class to subclass. If you are writing a team game, you'll want to subclass UTTeamGame. If you are writing a game without teams, use UTDeathmatch. If you are writing a game that departs significantly from any previously styled game type (but exist within the Unreal Tournament game), use UTGame. Subclassing is very beneficial: you immediately inherit all of the code in your parent classes which you can then extend or override to achieve your design goals. Of course, if you are writing a Total Conversion (TC) - which will depart completely from the UT game - you will want to subclass GameInfo. Lets look at a very simple Game Type mod:class ExampleGameType extends UTDeathMatch; defaultproperties { Acronym="EX" MapPrefixes[0]="EX" Description="Example GameType" }The above code, when saved in a file called ExampleGameType.uc is the shell of a new GameType. The only difference here is that we've changed the description to "Example GameType." This new name will be reflected in many places: the Practice Session selection window, the Scoreboard header, and so forth. If you play this game, it'll play just like any other Deathmatch Game, as no new behavior has been implemented.
Configuration
Similar to Mutators, a configuration file will be made for your package, with references to your game type. The following information is relevant:FriendlyName=Example GameType Description=An Example GameTypeLike Mutators, some configuration changes must be made in order to get the new game type to properly show up in the menus. You will need to provide text in your mod package localized text file, as follows:
;/////////////// ; Game Modes ;/////////////// [ExampleGameType] Description=An Example GameType. GameName=ExampleGameType EndOfMatchRulesTemplateStr_Scoring=First one wins! EndOfMatchRulesTemplateStr_Time=Most wins! ;/////////////// ; GameType Names ;/////////////// [ExampleGameType UIDataProvider_GameModeInfo] FriendlyName=Example GameType Description=An Example GameType.The Instant Action and start Multiplayer menus look in all localized text files for packages with section names that relate to the classes that have localized strings. If you add these lines to your package's localized text file, you'll get an entry called "Example GameType" in the list of games. The name is taken from the FriendlyName variable of your GameInfo class. The Description line gives your gametype a line of descriptive text on the menus. You probably don't need to worry about that right now. With a simple GameType we can take a look at a few of the methods available in GameInfo.
A first look at GameInfo
The Engine's GameInfo class is the definition of the basic game logic. At the top of the file you'll see a long list of variable declarations. Many of these variables have comments that describe their purpose. Below the variable declarations come the functions (or methods) that do the work. The first thing to look at is theTimer
function. In GameInfo it's pretty short, but in UTDeathmatch its very long. Timer
is a special UnrealScript event. If you call the function SetTimer(int Time, bool bLoop)
you can set up a repeating timer on your Actor. The Time parameter describes when the Timer function should be called. The bLoop parameter describes whether or not Timer should be called in a loop after the first call. All UTGame classes use a Timer loop of one second. This means that the Timer function is called every second. You can use Timer for events that have to happen at certain times. By declaring watch variables that count up seconds, you can perform events at any time up to a second's resolution. UTDeathmatch uses this to check and see if the TimeLimit has been hit in a game.
Another important time function to get to know is Tick
. Tick isn't used in GameInfo, but any Actor can use it. The declaration for Tick
is: Tick(float DeltaTime)
. Tick
is called on every Actor in the game each frame. DeltaTime contains the amount of time that has passed since the last Tick. Using Tick
, you can perform behavior that has to be done at less-than-a-second resolution. You must be careful not to perform CPU heavy behavior in Tick
, because it is called so often.
Further down in GameInfo is the Login
function. This function is called by the engine whenever a player logs in to the game. GameInfo's version of login does important setup stuff like assigning the player a name, a skin, a mesh and so forth. It also spawns the intial teleport effect and finds a spawn point to stick the player at. After Login
is Logout
. It is called whenever a player leaves the game. You can use logout to clean up after a player exits.
Another interesting function in GameInfo is AddDefaultInventory
. This function assigns a player his initial weapon and equipment. In UTDeathmatch, the player is given an ImpactHammer and an Enforcer. You can use AddDefaultInventory to add custom inventory to players that join your mod (for example, you might want to give them a grenade and some money).
The FindPlayerStart
method searches the actors in a level for NavigationPoints suitable for spawning. The PlayerStart Actor that a Level Designer adds to their level is one such location. In UTTeamGame, FindPlayerStart
spawns players and bots depending on their Team. It checks the Team of each PlayerStart and the Team of the Pawn to be spawned. You can use FindPlayerStart
to write custom spawn code (for example, you might want to spawn Opposing Forces in one location and Friendlies in another).
The RestartPlayer
method is called whenever a player respawns. The basic GameInfo version calls FindPlayerStart
to find a starting spot, moves the player to that spot and spawns a teleport effect. It also restores the players health, sets the player's collision, and gives the player his default inventory.
The Killed
method is very useful. It is called whenever a player kills another player. It looks at the cirumstances of the death (whether a player suicided or killed successfully) and the type of damage and prints a message. Finally, it calls ScoreKill
.
ScoreKill
awards points for a kill. UTDeathmatch assigns a frag for a successful kill and subtracts one for a suicide. UTTeamGame also adds a point to the TeamInfo of the Killer's team, or subtracts one in the case of a suicide.
DiscardInventory
is called whenever a player dies or is removed from the game. DiscardInventory
goes through a Pawn's inventory, tossing out weapons and destroying others as appropriate. You might override this function if you wanted to toss out a backpack or a trap.
Finally, the EndGame
function is called with a reason whenever the game ends. You might want to perform special logging or clean up here.
So that's a quick look at the more important GameInfo functions. The advanced GameInfo classes like UTDeathmatch add important new behavior for controlling bots and single player games, as well as refining the GameInfo methods into specific rules.
Again, the best way to learn how to make the most of your GameType is to read the code in the gametypes that ship with UT.
Game Rules
Heads Up Display (HUD)
Scoreboard
AI
How to Make Custom Content
Characters
See the Creating Custom Characters for UT3 page for more information.Levels
Again, some configuration changes must be made in order to get the new level to properly show up in the menus. You will need to provide text in your level's localized text file, as follows:[DM-Example UIDataProvider_MapInfo] FriendlyName=<Strings:UTGameUI.FriendlyMapNames.DM-Example> NumPlayers=4 to 8 players
Pickups and Weapons
Coming soon.Vehicles
Coming soon.A Few Things to Watch Out For
Here is some information on some major "gotchas" that mod developers have run into when writing mods for Unreal Tournament. A lot of this information may not be relevant to you until you have more experience with the engine. Learning a new game technology can be a very cool experience, but also a very frustrating one. Here are some pointers to ease your exploration. As a rule, always think about the performance implications of code you write.Accessed Nones
Sooner or later these will start showing up in your log files. UnrealScript treats Accessed Nones as warnings but you should treat them as errors. Accessed Nones are easy to fix and always signal that something is wrong with your code. If you are familiar with C++ or Java, it's easy to figure out what an Accessed None is. For those who aren't so familiar, here is a brief explanation... UnrealScript is an object oriented-programming language. When you write a program in UnrealScript, you define a set of behavior for these objects to obey and how they will interact in the game. An object has a set of properties: member variables and member functions. In order to access an object property, you need a reference to that object. Here is some sample code:class ExampleInfo extends Info; var PlayerReplicationInfo PlayerInfo; function PlayerReplicationInfo GetPlayerInfo() { return PlayerInfo; }Here we have a simple object called ExampleInfo that is a subclass of Info. It has two properties: a variable called PlayerInfo and a function called GetPlayerInfo(). You might want to interact with this object from inside your mod. Let's say you have a reference to a ExampleInfo object inside your mod and you want to get some information from inside the PlayerInfo property. You might write code that looks like this: class MyMod expands TournamentGameInfo;
function string GetPlayerName() { local ExampleInfo TheInfo; local string PlayerName; TheInfo = GetMyObject(); PlayerName = TheInfo.PlayerInfo.PlayerName; Log("The player's name is" @PlayerName); }In this example we call a function called GetMyObject() to get a reference to an ExampleInfo. We then access that reference to resolve PlayerInfo (TheInfo.PlayerInfo) and then access the PlayerInfo reference to resolve PlayerName (PlayerInfo.PlayerName). But what if there isn't a TheInfo available, or a bug in GetMyObject() causes it to fail to return an ExampleInfo? In that case, the function would return
None
. None is an empty reference - a lot like a NULL pointer in C++.
If, in our example, GetMyObject() returns None
, then the variable TheInfo is assigned None. In the next line, we try and access TheInfo to resolve the PlayerInfo reference. But TheInfo is None - it doesn't refer to anything. It cannot be accessed, so the Unreal engine logs a warning saying the code broke:
Accessed None in ExampleMod.GetPlayerName!Its very easy to avoid buggy code like this. Just add some checks to your code and define special behavior in the case of a mistake:
class ExampleGameType extends UTGame; function string GetPlayerName() { local ExampleInfo TheInfo; local string PlayerName; TheInfo = GetMyObject(); if ((TheInfo != None) && (TheInfo.PlayerInfo != None)) { PlayerName = TheInfo.PlayerInfo.PlayerName; } else { PlayerName = "Unknown"; } Log("The player's name is" @PlayerName); }Now we are checking to see if TheInfo is
None
and then checking to see if the PlayerInfo reference is None
. if
statements in UnrealScript use short circuit logic. That is, if
statements are evaluated from left to right. As soon as the code encounters a statement that negates the If
, it stops evaluating. That means that if TheInfo is None
, the code will never evaluate the following statement:
(TheInfo.PlayerInfo != None)It knows that it doesn't matter what the rest of the statement says, the first part is false so the entire statement is false. Accessed Nones can be especially dangerous in time critical functions like
Timer()
and Tick()
. It takes a lot of time to write out an error message to the log and if your code is dumping 3000 error messages a second it can really kill performance (not to mention disk space).
Iterators
UnrealScript implements a very useful language feature called Iterators. An iterator is a datatype that encapsulates a list. (UnrealScript only supports list iterators, future language versions may support user-defined iterators). You can get an iterator and loop on it, performing an operation on every object inside the iterator. Here is an example:local Ammo A; foreach AllActors(class'Ammo', A) { A.AmmoAmount = 999; }In this example we are using the
AllActors()
function to get an Actor List iterator. We then use the foreach
iterator loop to perform some behavior on every object the AllActors function returns. AllActors()
takes the class of the type of Actor you want and a variable to put it in. AllActors()
will search through every Actor in the current game for the objects you want. Here we are saying set the AmmoAmount of every Actor in the game to 999.
This sounds great, but consider the side effects: we are searching through a list of hundreds of Actors for a small few. This isn't exactly a fast operation.
Iterators can be extremely useful if used carefully. Because they tend to be slow, you'll want to avoid performing iterations faster than a couple times a second. There is almost never a reason to perform an AllActors()
iteration inside of Tick()
or inside of other loops. Use your best judgement.
The most common type of AllActors()
search you'll work with will probably be a search for all of the PlayerReplicationInfo Actors. PlayerReplicationInfo contains important information about Players that the server sends to each client. It allows each client to have an idea of the status of other playes without sending too much information. Its used to show the scores on the scoreboard and other common things.
Usually, there will only be a handful of PlayerReplicationInfo Actors in the global Actor List. It doesn't really make sense to do a time consuming search for so few results. In order to simplify this common iteration, there is a PRI array defined in the GameReplicationInfo class. Every tenth of a second, the PRIArray is updated to contain the current set of PlayerReplicationInfos. You can then do your operation of the PRIArray without having to do an AllActors()
call.
Other iterators are also available. Look in the Actor class definition for information. They do exactly what they sound like: TouchingActors()
returns touching actors, RadiusActors()
returns all the actors in the given radius, etc. Intelligent use of these iterators will help you keep your code fast.
Tracing
Because the Unreal engine does not use a potentially visible set, if you want to find something in the world in a spacial sense, you'll need to perform a trace. Most of the time you'll have a good idea of where you are tracing, you just want to know whats on the other end of the line. Other times, you'll use a series of traces to get an idea of what surrounds the object in question. First advice is to avoid traces wherever possible. Think very hard about what you are using the trace for and try to come up with an alternate way of doing it. Traces are expensive operations that can introduce subtle slowdowns into your mod. You might have a player doing a couple traces every tick and during your testing everything is fine. What you don't realize, is that as soon as you are playing online with 15 or more people online, those traces start to add up. If you have to perform traces, limit their size. Shorter traces are faster than long traces. If you are designing a new Shotgun weapon for UT, for example, you might want to perform 12 traces when the weapon is fired to figure out the scatter of the gun. 12 traces is perfectly reasonable; it's not like the player is going to be firing his shotgun 30 times a second. However, those 12 traces could get expensive if your mod uses large open levels. Its highly unlikely your shotgun is going to be very useful as a long-range weapon, so you might as well cut off its range at a certain point. It saves the engine from having to trace from one end of the map to the other in the worst case. Using traces is ultimately a judgment call. It really only becomes a big problem when you perform a lot of traces in a single frame. Nonetheless, it's definitely something to keep your eyes on.Replication - Decryption and De-obfuscation
Understanding replication is one of the most difficult aspects of writing a successful Unreal Engine game mod, but its utterly necessary if you plan on having any multipler gameplay at all. Most documentation is very highly technical in nature, can can be difficult to digest. Some of the basics will be covered here; and over time you will gain a deeper understanding and appreciation of replication.simulated
functions are called on both the client and the server; but only if called from a simulated function. As soon as a function call breaks the simulation chain, the simulation stops. Be very aware of what you are simulating and what you are doing in simulated functions. Never add a function modifier like simulated
just because you saw it in the Unreal source code somewhere else. Understand why you are adding it, know what it does. You can't possibly expect to write quality mods if you don't know what your code is doing.
Because a simulated function is called on both the client and the server you have to be particularly aware of what data you are accessing. Some object references that are available on the server might not be available on the client. For example, every Actor has a reference to the current level as well as the current world that it belongs to. Inside the world reference is a reference to the current game. You might write code that looks like this:
simulated function bool CheckTeamGame() { return World.Game.bTeamGame }This is a simple simulated function that returns true or false depending on whether or not the current game is a Team Game. It does this by checking the bTeamGame property of the current level's GameInfo reference. But there is something wrong... The Game property of the Level reference is only valid on the server. The client doesn't know anything about the server's game object so the client will log an Accessed None. If you open up the script for LevelInfo, you can find a section that looks like this:
//----------------------------------------------------------------------------- // Network replication. replication { reliable if( Role==ROLE_Authority ) Pauser, TimeDilation, bNoCheating, bAllowFOV; }The replication block is a special statement that tells the Unreal engine how to deal with the properties of this object. Take a closer look. First, we have a replication condition:
reliable if( Role =
ROLE_Authority)=. The first part of the condition will either be reliable or unreliable. If it says reliable, that means the engine will make sure the replicated information gets to each client safely. Because of the way the UDP protocol works, its possible for packets to get lost in transmission. Unreliable replication won't check to see if the packet arrived safely. Reliable replication has a slightly higher network overhead than unreliable replication.
The second part of the condition (Role =
ROLE_Authority)= tells the engine when to send the data. In this situation we are going to send the data whenever the current LevelInfo object is an Authority. To really decypher what this means you have to understand the specific role of the object in question. With a LevelInfo, the server is going to maintain the authoritative version of the object. The server tells the clients how the level is behaving, not the other way around. For our example replication block, this means that the data will be sent from the server to each client.
The other common type of condition is (Role < ROLE_Authority)
. This means that the engine should send the data when the current object is not an authority. Or rather, that the client should tell the server the correct information.
Finally, we see four variables listed beneath the condition. These are the variables that the statement applies to. In this situation, we have a statement saying, "If we are the server and the client has an outdated copy of these variables, then send to the client new information about Pauser, TimeDilation, bNoCheating, and bAllowFOV. Always make sure the data arrives safely."
The replication statement doesn't cover the rest of the variables in the LevelInfo. This can mean two things. Either the information is filled in by the client in C++ (in the case of TimeSeconds) or the information is never updated on the client and is completely unreliable (in the case of Game).
You don't have access to the C++ source code, but you can make a couple inferences about an object's properties to help you determine whether or not a class has non-replicated properties that are filled in natively. Look at the class declaration for LevelInfo:
class LevelInfo extends ZoneInfo native;
native
means This object is declared in C++ and in UnrealScript. Native classes probably have behavior in C++ that you can't see. Only a few special classes are native - typically for performance reasons.
Finally, watch out for classes that say nativereplication
in the class declaration. This means that the replication
block inside UnrealScript doesn't do anything and that replication is entirely defined in C++. Some network heavy objects use native replication to help with network performance.
So now you have an idea of how to avoid problems with replicated variables and simulated functions. Now lets look at replicated functions.
A replicated function is a function that is called from the client or the server but executed on the other side. An example of a replicated function is the Say
function. When you hit the T key to talk to everyone in a game, you are actually executing the Say function along with whatever you said. The client takes the function and its parameters and sends it to the server for execution. The server then broadcasts your message to all the other clients.
Replicated functions are very easy to use if you remember one thing: they can't return a value. A replicated function is transmitted over the network to the other side...that takes time (approximately equal to your ping). If replicated functions were blocking (i.e.: they waited for a return value) network communication would halt.
This is obvious for anyone who thinks about it, but when you are working on your mod you might not think about it. Replicated functions return immediately. Use them to trigger behavior on the client (like special effects) or send a message (a weapon fire message to the server).
Finally, replicated functions are restricted to only a few classes. A function call on an Actor can only be replicated to the player who owns that Actor. A function call can only be replicated to one actor (the player who owns it); they cannot be multicast. You might use them with weapons or inventory items you make (where the function is replicated to the player who owns the item).
That should help you with the basics of replication.
Examples
Mutator
Game Rules
Overlay
GameType
Game Rules
HUD
Scoreboard
AI
Deathmatch Level
CTF Level
Custom Characters
To assist in getting started with creating custom characters for Unreal Tournament 3, the following character skeletons are available for reference:- UT3_Male.max (3D Studio Max 9)
- UT3_Female.max (3D Studio Max 9)
- UT3_Krall.max (3D Studio Max 9)
- UT3_Corrupt.max (3D Studio Max 2009)