Unrealed Reference Collection

Unrealed Reference

A collection of essential reference materials

Assembled by: ekoSonic
ekosonic@planetunreal.com

[osX] Website
Visit the Unrealed Tools Page
at the [osX] Site for downloads
and links regarding Unrealed.


Editor Tool Buttons

    Tomasz ‘Millennium’ Jachimczak
    (tomasz@planetunreal.com)

    Basic Building Brushes:

    Add brush to world. This button is used to "add" a brush to the world. Any brush can be added to the world, but make sure that you don't try to add someting to a peice of the world that is allready solid, as this can turn out quite nasty when playing the game or rebuilding...

    Subtract brush from world. This button will "carve" out the brush from your level. Do not try to subtract a brush from an area that has allready been subtracted - as this won't work and can have adverse (bad) effects on your level.

    Brush intersection. This is the "intersect" button. It is used to change your brush. Say you have a "cube" brush, but only want to cut out half a cube from a solid wall. You could go through the cube properties and change the shape of your brush, or you could simply move the cube to where you want it to go and then press this button. This will make a new brush for you - but it will only be where the solid wall was before. It is also good for making complex objects into a single brush. Say you want to make a fireplace, and you want to use it in a few places - create it (bit by bit) and then place a cube brush over the top (so the cube {or whatever} encompases the fireplace totally) and when you press this button, your new brush will be EXACTLY the same as what you created using many little bits.

    Brush deintersection. This does the opposite of what the intersect button does. It will select the parts of the brush that are in an area that is not solid - and create them into a new brush for you.

    Add Special Brush. This allows you to create "special" brushes to your map. These can be a wide range of things, such as windows, water levels, portals and the like. It is normally used in combination with the "sheet" brush.

    Add movible brush. This button will create a mover for you. It will use whatever brush you have currently selected. When using this brush however, make sure tht the brush you have selected has NOT been streched in any way, as it won't work properly...

    Build a cube. This will create a cube brush for you. If you right click on it, it will give you an option to go to cube properties. This will allow you to change the height, depth ect of the cube brush you will create.

    Build a Sphere. This is the same as the cube, but it will create a round ball shaped (well a sphere really) brush. Once again right click on it to alter the properties of the brush.

    Build a cylinder. This will create a cylinder for you, that you can use like any other brush.

    Build a cone. This will create a cone (rounded pyramid) that you can use like any other brush.

    Build a stair. This will create a straight flight of stairs. These stairs will not turn or curve as they go up.

    Build a spiral stair. This will create a rounded set of stairs that can be set to form a very tight spiral staircase.

    Build a curved stair. This will simply create a rounded set of stairs that are similar to the first set, but they curve around to a side when they have been built.

    Build a sheet. This creates a 2 dimensional sheet. It can be used for a VERY LARGE number of applications. One of the most common is to create a water plane. This tool does however require more pratice then the other brushes, as it is not a 3D brush and can easily "wreck" a level. It should NOT (I can't stress this enough) be used to create walls and other solid shapes. (Well at least until you are quite familiar with the editor).

    Advanced Building Brushes:

    Move Camera. This is the normal view button. It is normally toggled to "on". It lets you move in the veiws. My advise is to leave it set to what it is...

    Zoom camera. I can honestly say I have no idea what this button is supposed to do. I have tried it a few times, but normally end up miles away from where I am building, so I always leave it set to the button described above.

    Rotate brush. This button is used to rotate the brush that you have currently got selected. It is used by holding CTRL and with a combination of mouse buttons (Left rotates X axis, right rotates Y axis and both rotate Z axis) allows the brush to be rotated to any angle.

    Sheer brush. This brush allows you to "skew" the brush. That is, it allows you to 'lean' the brush to a side.

    Scale brush. This button will allow you to change the size of your brush. That's right, once again CTRL and the mouse will activate it when it's selected.

    Stretch brush. This will alter the size of your brush, but it will do it one axis at a time. Which basically means it's great for adjusting something that is almost perfect.

    SnapScale Brush. This button will allow you to stretch a brush along all three axis' , but at the same time it will "snap" to the grid. This is VERY handy to enlarge or shrink brushes, as they are easy to match up later.

    Select all polys. This brush will select ALL the things in your map. I don't know what the point is, but the option is there...

    Select all actors. Once again, I am not sure. But look on the bright side, I haven't used it - and still made great maps, so it probably (feel free to prove me wrong) isn't all that integral to the editor.

    Select actors inside brush. This is another of the buttons I haven't used very much - or at all. I can't really tell you what it is used for...

    Select none. This will make sure that "none" of the brushes or textures are selected. In a way, it 'unselects' everything...

    Invert actor selection state. This button will replace the selection that you currently have. Sorry I can't say more, but yet again, it's a brush that I have little use for.

    Replace selected brushes. This button will replace the brush with what you currently have selected.

    Replace selected non-brush actors. This button will replace the actors selected with what you currently have selected.

    Undo last operation. I don't think that anyone there needs a detailed explanation of what an "undo" button does, But on that note - just remember that you can't undo past a rebuild. I mean that if you rebuild your level and don't make any changes - then you won't be able to undo something that was changed before you last rebuilt.

    Redo operation. If you accidentally pressed "undo" once too many, then this button will redo the last action that you undid.

    Pan textures. This will move the texture that is selected. It is used mainly for aligning the texture to match up with other textures. If you have more then one brush with a texture, and you want them to line up - then this is the tool for you.

    Rotate textures. This button will allow you to rotate a texture so that it matches the direction of other textures - or simply faces the direction that you want it to face.

    Drawing Region: Selected Actors. This button will "hide" all the brushes that are in your level. It's great for adding in detail and things like that when you don't want to see all the other brushes.

    Hide Seleced Actors. This button is similar to the one above, but it will only hide the brushes that are selected. You can select more then one brush at a time to hide by pressing CTRL and select as many as you like.

    Drawing Regions: Off. This will "bring back" any brushes that were hidden using the buttons above.

    Drawing Regions: Brush Z (Depth). This will hide the Z axis apparently. I can put this into the pile of buttons that I never use.

    Toggle vertex snap. This will toggle the grid snap function of placing brushes. It is generally used for adding the details when the grid is in the way...

    Get Help. Well, now here is a useless button - as you are probably using the BETA UnrealED, this button has absolutely NO function whatsoever, so don't bother trying to find the help file...

    Camera speed. This will change the speed with which your moves moves brushes and other objects in the views.

    Toggle grid mode. This button will snap objects to the grid when they are moved around in the editor.

    Toggle rotation grid. This will toggle whether you want rotational grid snapping. This button makes it rather difficult to rotate a brush when it is large and also makes it difficult to align things to angles made by sides of cylinders for example, but makes it a LOT easier to align general angles in the editor.


UnrealED Tool Bar

Tool Bar

This is the Tool Bar with its Icon's labeled
  1. Move camera
  2. Zoom camera
  3. Add brush to world
  4. Rotate brush
  5. Shear brush
  6. Subtract brush from world
  7. Scale brush
  8. Stretch brush
  9. Brush intersection
  10. Snapscale brush
  11. Select all polys
  12. Brush deintersection
  13. Select all actors
  14. Select actors inside brush
  15. Add special brush
  16. Select none
  17. Invert actor selection state
  18. Add movable brush
  19. Replace selected brushes
  20. Replace selected nonbrush actors
  21. Build a cube
  22. Undo last operation
  23. Redo last operation
  24. Build a sphere
  25. Pan textures
  26. Rotate textures
  27. Build a cylinder
  28. Drawing region: selected actors
  29. Drawing region: off
  30. Build a cone
  31. Hide selected actors
  32. Drawing region: brush Z(depth)
  33. Build a stair
  34. Nothing
  35. Toggle vertex snap
  36. Build spiral stair
  37. Get help
  38. Camera speed
  39. Build curved stair
  40. Toggle grid
  41. Toggle rotation grid
  42. Build a sheet

Wolf's Unreal


Shortcut Keys

Editor Shortcut Keys

This is a list of short-cut keys that can be used in the editor to stop all the bother with mouse clicking. Some of them are listed in the menu, but others are not, so read through them, and I think you will find a few nice "easter eggs" in here that will make your editing life a lot easier.

Delete : Delete selected actors.

1, 2, 3 : Slow, medium, and fast movement speed.

F1 : Help.

F4 : Actor properties.

F5 : Surface properties.

F6 : Level properties.

F7 : Compile changed scripts.

F8 : Rebuilder.

B : Toggle brush visibility in the current viewport.

H : Toggle actor visibility in the current viewport.

P : Toggle player controls or realtime update of the viewport.

Shift-A : Select all actors.

Shift-B : Select all surfaces belonging to the selected surfaces' brushes.

Shift-C : Select adjacent coplanar polygons.

Shift-D : Duplicate selected actors.

Shift-F : Select adjacent floors.

Shift-G : Select surfaces belonging to the selected surfaces' groups.

Shift-I : Select surfaces with items matching the selected surfaces' items.

Shift-J : Select all adjacent surfaces(flood-fill).

Shift-L : Look ahead in the current viewport (no pitch or roll).

Shift-M : Memorize selected surfaces.

Shift-N : Select none.

Shift-O : Select surfaces from intersection of selected surfaces and memory.

Shift-P : Select all surfaces.

Shift-Q : Reverse the set of selected surfaces.

Shift-R : Recall memorized surface selection.

Shift-T : Select all surfaces which use the current texture.

Shift-U : Select union of selected surfaces and memorized surfaces.

Shift-W : Select adjacent wall surfaces (flood-fill).

Shift-X : Select exclusive or of selected surfaces and memorized surfaces.

Shift-Y : Select adjacent slanted surfaces.

Shift-Z : Select none.

Ctrl-A : Add brush to world.

Ctrl-B : Load brush.

Ctrl-C : Copy.

Ctrl-D : Deintersect brush with world.

Ctrl-E : Save as.

Ctrl-L : Save level.

Ctrl-O : Load level.

Ctrl-P : Play level.

Ctrl-N : Intersect brush with world.

Ctrl-R : Redo.

Ctrl-S : Subtract brush from world.

Ctrl-V : Paste.

Ctrl-W : Duplicate.

Ctrl-X : Cut.

Ctrl-Z : Undo.


Getting Started With UnrealEd

The first thing you'll need to do is download UnrealEd. The link to the download page is: http://planetunreal.com/unrealed. At the time of writing, the present version of UnrealEd is Update 4. Install this version into the system directory of the Unreal engine game you are working with.

UnrealEd is the complete development program for Unreal Engine games. You can use it to create maps, texture packages, sound packages, music packages, and code packages. You will find that there aren't really any other content-creation utilities for the Unreal engine, because UnrealEd covers the whole gamut. (The sole exception is importing models, for which you will need a program like 3ds2unr, used in conjuction with the command line UnrealScript compiler, UCC.)

You can use UnrealEd with the current Unreal Tournament demo. It is fully functional. The demo will not load any third-party maps -- other than this, content created with UnrealEd is loadable in the demo.

Once you have UnrealEd installed, there is one other thing of vital importance: the UnrealScript Reference, written by Tim Sweeney. Click the link, and print it out. It will print out to about twenty pages. After a few months with UnrealScript, I'm onto my second copy of this reference; the first one being illegible from tatteredness and annotation. I'm not overstating the importance of this doc.

Three sites primarily administer all things UnrealEd related:
http://www.unrealed.net
http://www.unrealed.org
http://www.unrealized.com

UnrealScript is the language in which most of Unreal (and UT) was coded. For modifications, 99% of the stuff you want to do is best done in UnrealScript (usually very easily).

You can view most of the UnrealScript source code from within UnrealEd (a few behind-the-scenes classes are not available for viewing, but all Actor classes are). In the right-hand tool bar, select Classes from the listbox. You will be viewing a hierarchical tree of the classes which subclass the Actor class. These are the classes you will be working with most of the time.

Note: UnrealScript is a object-oriented language, modelled on a pointerless C++ with Java stylings. If you are unfamiliar with object-oriented languages, UnrealScript is a gentle introduction to it. You'll pick it up pretty much as soon as you are prepared to think a little differently about your programming :) I wrote a tutorial on creating a spell system in UnrealScript which serves as a useful basic introduction to object-oriented programming: http://www.planetunreal.com/chimeric/scripts/tut42.htm. Also check out Catalyst's very thorough examination of the ideas behind OOP and UScript: http://www.catalyticconversions.com/codezone/script.htm.

You should print out several classes. In my opinion, print out Actor, Inventory and Weapon, as you will inevitably be handling these classes whatever you do. If you've got the ink, print out Pawn as well. Object, Actor and Pawn are the 'big three', IMO. If you are intending to work with mutators, print out the following for your reference: GameInfo, DeathmatchPlus, Mutator. You will be working with a blindfold if you don't read the Actor class, and preferably reread it every week for a couple of months :).

You should have a pretty big wad of paper by now. There are a few more essential references:

http://unreal.epicgames.com/UnrealClasses.htm -- I've never seen a link to this doc, but it is incredibly useful.

http://unreal.epicgames.com/Console.htm -- console commands; needed for testing your code.

http://unreal.epicgames.com/Packages.htm -- guide to Unreal packaged content creation.

http://www.planetunreal.com/chimeric/references/beginners_guide1.html -- ca's excellent beginner's guide to Uscript.

And just add this site to your bookmarks: http://unreal.epicgames.com. It's the font of knowledge.

Now that you have all the references in a nice folder (yes, you do), you can start messing with the code. You never edit the existing UnrealScript source code, never. You subclass the existing class which contains all the code upon which your new class relies, and you take it from there. That is the golden rule of UnrealScript programming. From there, you are away.

To compile your code (on average every six or seven lines of code), hit F7. Make sure you correct any errors before moving on. If you set up a huge system for doing something, then get a thousand compile errors because your system is not valid in Uscript and you need to modify it, it's a huge let-down. Compile regularly and thoroughly.

To save your package (the .u file which contains your code), hit the Save button below the class tree and choose your package from the listbox. Saving any of the Epic packages, accidentally or deliberately, usually results in having to reinstall everything. You can import text files containing UnrealScript code (saved with a .uc extension), and export all your code to .uc text files, using the Import and Export buttons respectively, below the class tree.

If you need to set any default values of editable variables for your class, hit the defaults button. Handy tip: to declare your own variables as editable:

var() int editableVar; //this variable is editable. You'll find it in default properties under the name of the class.

var(anyword) int editableVar; // You'll find this variable in default properties under the 'anyword' category.

var int nonEditableVar; // this is a variable which is not to be found in default properties.

Once you written a new class which has the Actor class somewhere in its lineage, you can add it to a game with the following command:

summon packagename.classname

For instance, if you had written a new weapon called, I don't know, NuclearBomb and saved it in the Nuke package, you could load it into a game by typing: summon nuke.nuclearbomb.

If you have written a new game type, you can start it by typing the following at the console:

open anymap?game=packagename.gametypeclassname

New playerclasses are loaded similarly, using class= instead of game= . You can load one or more mutators by using the mutator= key, separating each mutator with a comma. Here is a full command line:

open anymap?game=pkg.class?class=pkg.playerclass?mut
ator=pkg.mutator1,pkg.mutator2

That should keep you going, I hope. If I've skipped over something, let me know and I will revise this tutorial.

Sites related to UnrealScript programming:

Chimeric

Unrealed.exe

UnrealEd.Org

UnrealEd.Net

the mutation device


Editing Everything

Unreal Editor Vocabulary

    Tomasz ‘Millennium’ Jachimczak
    (tomasz@planetunreal.com)

    I will start this tutorial by explaining a little of how the editor actually works. This editor is unlike many other editor that are around. It also differs from many 3D Programs taht are available for purchase. You will find that most programns that are available will start the screen off with a huge "empty" area. Thus if you would want to build something, you will have to "add" to the world. What makes the Unreal Editor different is taht the world that you start off with is actually a huge great SOLID block. It does not have any empty or blank space in it. Thus if you would like to create a room in the world, then you actually have to "subtract" it from the world. If this may seem a little "backwards" with the way that most editors work, then I agree with you, but at the same time, I do also think that this does make a lot of sense. In most of the other editors, you would have to create a solid "wall" to show the last part of a level (or world) before the editor would allow you to actually see it. Thus you would hahe to create a large number of addition brushes simply to have a finite size of the level. With the Epic Editor (for Unreal) you do not have to create a "final" barrier to your world, but simply each time you subtract a room (or space) from the editor, then it automatically adds to the area that you can travel in the game. Thus, it does make our lives somewhat easier, and also does make a smaller load on the processor of the PC playing it, as it does not have to count and use as many brushes, as if you would have extra brushes to mark the final point of a level. Ok, now that we know a little of how the editor works, let's get into some of the more technical words that you will probably hear mentioned a lot in my tutorials and other people when they talk about the editor. The amount of detail I will go into will vary on how important I think something is, and if it is really important that you know what each word means.

    • Brushes

      • Solid Brushes
        A solid brush is a brush that has been placed into the level, and the builder brush that created it was solid at the time. When you select the properties of a brush (from a primitive) you will be able to select one of two options. You can choose SOLID which means that the brush you create is a 3D shape that has got no "insides" to it. It is a basic shape that has got an outline, but no "inside" to it. If you add a brush like this, you will add in a new section to the world, and there will be no way to get into it.

      • Hollow Brushes
        A hollow brush is a brush that when created, actually is an outline, and has got "another smaller shape" inside it. Thus when you add (or subtract) it into the level, you will have a shape that appears hollow on the inside. This gets hard to explain here. Let me use an example here. If you create a cube that is hollow, you will actually have a brush that is more like a cardboard box, rather then a childs palying cube, that is entirely solid in the middle.

        • Builder Brush
          The builder brush is actually the RED BRUSH that you first see as a red cube when you open the editor. This will be your most important tool when you are creating and editing levels. While it might seem a little "primitive" at first, you should find that with a few of the things that you can do with it, you will be making it into whatever you like in no time al all. If you choose to click on any of the primatives (The shapes on the toolbar to the left of the screen) then your builder brush will change to that shape, and you can then add and subtract with that brush.
          This Tool, is olny a temporary brush, and does not actually make any difference in the level itself. When you are "rebuilding" your level, you will find that it does not affect anything at all, as it is really only "a fake"until you use one of the other tools to make it permanent.

        • Subtracted Brush
          A brush that has been subtraced will show on the editor 2D views as an orangy brown color. This has been made from the builder brush at one time, and then the subtract tool has been used, to permanently place it in the level. If this brush has been placed in an area that was solid before, then you will be able to travel into that section of the game. If you subtract a hollow brush into the game, then you will be able walk inside the brush.

        • Added Brush
          When you "add" a brush into the world, you are actually creating another solid. Thus, you can only "add" a brush into the world where there is a hollow area, otherwise you will be adding something to a place where something already exists (This should mean completely useless) When you add a brush to the level, you will see it appear as a Bark Blue shape in the 2D views.

        • Special Brush
          A special brush can be added to just about anywhere in the world. You will see them as Green colored shapes in the 2D views. Special Brushes can be used to add water, a transparent "barrier" and many other useful things, although you will most likely find these out either for yourself, or as you start reading some more of the tutorials.

        • Mover Brushes
          A mover brush is a brush in the world that will actually move. This can be created by either joining a "trigger" to the mover, or by touching the mover, but that is all explained in the related tutorials. You wil not see the textures on a mover in the 3D view, but only see the purple outline and there will be no way to change the texture alignment or anything like that, so you have to be sure that everything is perfect before you create it.

      • Movers
        Movers are brushes that can be set to move in the level, and are explained in the Brushes section.

    • Triggers
      A trigger can be used for a lot of things. The most common use it to activate a mover with a trigger. A trigger will appear in the editor as a little question mark next to a cube. To set a trigger to activate a mover, simply change the "tag" of both trigger and mover to be the same. Triggers are explained once again, in tutorials, so I will not go into to much detail here, although they can be used for just about anything in your level (world).

    • Intersecting/Deintersecting
      You can create many things with the primatives that are supplied in the editor, although a lot of the time you find that your geometry will overlap. If you have things that overlap, you can find that your level may not rebuild properly. You can find that things will appear to have large "black" area that do not work properly. You can however use the two tools (intersect/deintersect) to make the builder brush either cut everything OUTSIDE the solid, or to make the builder brush appear only within the brush (s). This can be rather tricky to explain here in text without being able to show a quick animation, but what it means is that means is taht your red builder brush will be "cut" to only what is either inside or aoutside the bushes that are in your level already.,/li>

    • Rebuilding
      When you have created a brush (or more hopefully ;) then you will have to compile it into an actual level. When you press the rebuild function (Either press F8, or choose rebuild from the options menu) setting it to "rebuild" will take a few seconds to quite a f ew minutes (depending on the complexity of the level) and it will be all ready to play. There are a few settings within the window that you can choose to either turn on or off but they are explained under their own heading, although there is a list of them here. BSP, LIGHTING, GEOMETRY, OPTIMISATION. When you rebuild make sure that you do save the level, as you will sometimes find that there may be errors and some of the can actually crash your system. Actually it is a good idea to get into the habit of saving the level quite often and having a few little "backup" files that are kept there just in case...

    • BSP
      This is what will decide where the player will be able to go, and where they will not be able to go. Any SOLID brushes will affect the BSP of the level. If you have got an area that has got a large amount of adding and subtracting in the same place, then you may experience something that is called BSP holes, wich appear as large black holes in your level, and most often also result in the player not being able to move through the area properly. If this is near the end of the level, or near an area that "ends" with a solid area, then you may also experience the dreaded HOM. (This is explained below).

    • HOM - Hall of Mirrors
      This is the bane of all editors. You will have most likely seent eh HOM before, but simply put it down to bad level editing. It is - kinda. You will most often see the HOM where you have got an area or even maybe a transparent texture that is in the last place in a level. Ok, this is hard to explain, but for example, if you have got a room in the level, and there is nothing beyond the room. You would normally place a texture on the walls that is not see through right? If you placed a texture that has got invisible areas, when you try to play the level, the game engine will try to "cover" the holes there, and it does it by "mirroring" the last thing shown there. If you ever find a shimmering area where things have stopped moving and it looks bad, then most likely this is what has occured. There is no "one way fix" to get rid of the HOM, but please check your textures to make sure that they do not contain any areas of transparecy, and that in the 2D views bits of your map are not dissapearing.

    • Lighting
      Lighting in the level is pretty basic. It simply means taht you will have some sort of illumination that is in your world so that the players will be able to see where they are and what they are doing. Lighting may however take the form of many different things. The easiest way to make a light is to right click in the level on a texture (in the 3D view) and choose "add light" from the drop menu. See also Dynamic lighting.

    • Dynamic Lighting
      Dynamic lighting will greatly enhance the look of your level, although it may actually slow down the level, and cause some less powerful machines to drop frames. Dynaminc lighting simply refers to lights that do things. You can create lights that shimmer, or lights that are spot lights and rotate, lights that pulsate, or lights that simply "strobe". There are a LOT of things that you can use, but as mentioned earlier here, they will use a large amount of resources (compared to a normal light that is) to use, and some lower end machines may suffer a large drop of frame rate.

    • Geometry
      Geometry refers to the actual brushes that are in your level. These are the things that your players will walk over, and also where they will be able to see. To learn how to create brushes and geometry, refer to the Brushes section of this document.

    • Optimisation
      There are a lot of questions asked about the "Optimisation" settings that can be changed within the rebuilder, although you should not actually change any of them in the BETA Editor that has been released by Epic with Unreal. I have played with the optimisation settings before, and the only good that it did, was to kill my level. Trust me on this one, until there is a fix for the settings - Do NOT change them.

    • Zones
      Zones can be created in the editor. Zones will be able to hold specefic areas in the map. A zone is really kinda like a level in itself. For example, if you have an area with water in it, it is actually a "water zone" or you can create an area with fog in it, and to make the level play faster, you cn restrict the area that the fog is visible from, then create a Zone around it. This will then allow the CPU resources to be used for other things in the game (like framerate).

    • Portals
      Portals can be created in the level, to allow players to simply "walk" (or fall, or fly or whatever) from one place in the level, to another place. These have a nice tutorial written on them, so I will not go into too much detail here.

    • Textures
      Textures are simply images that are used to "paint" the brushes in your level with, so that people can actually see what is stopping them from going forward, or what is stopping them from falling through the ground.

    • Wave or .wav
      This is a format for storing sound. It is the basic format that most people use to import sounds into the game and then save them for use in the levels. Wave files can be explained much more thoroughly in the Booklet of your Sound-Card, so I will not explain them here in any more detail.

    • Texture Packages or .utx
      These are simply files that contain many images for use as textures in the levels. There are tutorials on this site, that will explain how to create texture packs and how to import files into the engine for use.

    • Scaling - Textures
      When you have a texture on the brush, you can actually change the size of the display. If you have an image that is too large, or small for the brush, you can actually change the scale in which it is displayed.

    • Scaling - Brushes
      Brushes just like Textures can be scaled to change the size of them, however, if you want to create a mover from anything, you CANNOT (and I can't stress that enough) used scales brushes during any part of the construction of the mover. If you do, everything will appear to be fine, until you actually try to use the mover or try to walk up to it. At this point, you will find that the brush that was scaled, will actually still be "unscaled" even though it may look like everything is fine.

    • Skybox
      The skybox is the part of the map that is dispalyed whenever there is a texture 9or face of brush) that has been set to "Fake Backdrop". This is most useful when you have to make a "sky" or a background that you want seen from any part of the map.

    • PlayerStart
      A Playerstart will be the place in your level that the player begins when you start the level. These can be placed just about anywhere you like, although it is a good diea to make sure that they are not ina place that the player can get hurt immediately.

    • PainZone
      A Painzone is an area of the map that is zones all around, and when the player walks into it, they will start losing health points. This loss of health will continue until the player either leaves the pain zone, or dies. Water is actually a painzone, although there is a delay in it. The delay is there to allow the player to use the air in his/her lungs (ie hold their breath) and when they start running out, they will start to lose health.

    • KillZone
      A Killzone is quite similar to the above zone, although rather then just making the player lose health slowly, they are actually killed in the spot. It does not make a difference if the player is in GOD mode or not. They will die as soon as they step into a Killzone.

    • Frame Rate
      Frame rate is really simple. It refers to the number of frames that the computer can show in a second. The higher the number, the smoother the display will be. To try to improve your frame rate on a level, make sure that there are as few a number of brushes and as few a number of Dynamic lighting as possible. There are a large number of influences that will determine the framerate, but generally speaking, try to use as little "goodies" as you can, and the level will play better.


Unrealed Tutorial One

Knowledge in Depth

    Tomasz ‘Millennium’ Jachimczak
    tomasz@planetunreal.com

    Forward

    Well, here goes a new tutorial. This tutorial will certainly be a lot longer then the others, as I will be going through the making of a whole level from start to a point where you should be able to take over and finish it off.

    While the level will be fairly basic, and not something that would win all the prizes at a contest, it will be a good way for you to start Unreal Editing. In these tutorials, you will find that I explain everything I do, and I will also track each thing with the most detail I can (Without going over the top).

    I have been receiving a fair amount of mail asking for things to be explained, and I will be trying to get to the level of detail that some of the visitors ask for. If you find that I am getting too bogged down, skip to the next bit, or ignore a sentence or two, but all in all, this should be a perfect guide to getting you not only started in the editor, but also making maps in general.

    Abstract

    I will be making a DM (DeathMatch) level here that will have a few nice goodies in it, and it will include heaps of things that people ask about. I will be offering downloads of the map in it's building stages, so you will be able to find out how I go about things, and what I have completed in a certain tutorial.

    I will also be placing links in this tutorial, to other tutorials that cover either similar areas, or things that may interest you. Please don't get too bogged down in reading the other things, as I will be explaining everything here, and only offer the other links as things that you may like to read at the time, to either get a better understanding of something, or to show you a place where you might find something that takes things a step further. In any case lets get this thing on the road eh?

    Assumptions

    In this tutorial I make no assumptions. It is a tutorial aimed at a beginner who has very little understanding, of the Unreal Editor, or someone who may have a decent idea of Level Design, but has little experience in UnrealEd.

    Note

    Unlike the other tutorials, this is not split into small sections with headings. It is however split into paragraphs that should be read completely before you get into the editor. When you finish reading the paragraph, try to go into the editor and do what was written without looking at the tutorial all the time.

    This will help you to remember and more importantly understand what I am writing here.

    Firstly (and most importantly) you will have to open the editor.

    If you get an error like 'Unexpected Error 50003' or some other error that won't let you open the editor to a normal screen, please head over to unrealed.net to attempt to solve the problem. Files that may be required to make the Editor work can be downloaded from the site, under the ‘Files’ section of the site.

    If you have got the editor working, please proceed.

    When you open the editor, this is what you should see. If you have used things like 3DMax or Lightwave etc, then it will most likely be quite familiar; otherwise, it will most likely be quite daunting.

    Actually, it's a good setup, and I will take a few moments here to explain what each thing does.

    In the left-hand side, you will see a menubar of sorts with heaps of little buttons on it. I will not explain here what each of the do, as it would be a little like saying things over and over, but unrealed.net does have a tutorial that does explain each of them and the Ued Help File also covers each of the functions. Take a look, and then come back. I will be explaining things as I go here anyways, so don't get too caught up in it. It will be made clear in this tutorial anyways.

    Next there are four windows that have a specific task each. They are windows that you will be using to view your map as you make it.

    • The top left one is the over head view. You will see your level in an architectural design here. It will be filled with lines, icons and all sorts of good things by the time we finish here.
    • The top right hand one is similar. It is the 'Map XZ' screen. This means that it basically takes a shot of your level from the front. It is also something that shows your level in 2D, using different colours of lines etc for different meanings.
    • The bottom right is once again a 2D view. This time it shows your level from the side. I could say left or right here, but the easiest way to explain from which side is this. In the top view, as things get further down, then they will appear to go towards the right hand side of the side elevation. Got that? It's a little hard to explain, but you should either get the idea now, or you will as soon as we start to make things, and use the screen to work things out.
    • Ok, there is a nice black screen left (Although there should be a few dark blue lines in it). This is the 3D view. It will be an invaluable guide to how your level looks, as it provides you with your window to the world in a rendered fashion. You can move through there and do almost anything you like. It's like having a little player move in 'Ghost' mode while you are playing, except you will also see where you have lights, icons, and how things work.

    Lastly, there is another toolbar on the right hand side. This is what is most often called the 'Textures Bar' or 'Browser Bar'. It is where we select textures to use, classes and actors to add, and all the other cool things that we will be using.

    Once again, I will be explaining this one as we go on. Trust me, the last one, there is a LOT of explaining to do with, so I will be making the most of it, but rather then say it all now, I know that you are getting anxious to get into the real editing right? Cool, so am I =) It's getting a little boring just typing all this in, so let's start on the level itself. More will follow on these screens when we need to use them ok?

    Now the first thing that will be needed here is a texture style. I will be making this level in a 'Natural' style, and there will be a few areas of a simple village in it as well, so I will be using textures that come with the Unreal engine (Rather then making my own so early in the level).

    If you haven't taken a look at the textures before, you may want to have a browse at what is available, but I will be making notes of each textures names as I use them. This will make it easier for you to make your level to the exact thing that I have done here. Ok enough banter, the first texture package that we open will be a set called 'Ancient'.

    In the editor, on the Browse Bar, you will see a button called 'Load'. Here is a quick image, if you are having problems (The middle is cut out to make it smaller).

    I have got a few steps here with nice blue numbers. I will be using these as a reference to what I am talking about. Ok, here is the list of what each one does...

    1. This is the folder from where you are opening the file. Most of the files that you will use should be located in the 'Unreal/Textures' folder, as this is where they will need to be so that when you go to play the game, it loads Ok, and the other machines knows where the files are.
    2. This is a list of the various texture packages that you have got on your hard drive. If you don't have all the ones that you see in my window, relax, as I have got quite a few that I have made, and I have also got others downloaded from the net, or packaged in files with maps in them. If you installed Unreal, you will have everything that you need.
    3. This is where the name of the file that we are after will be. It's very much a normal window, but I may as well explain it in detail right? That's why you are reading this tutorial =)

    As you see in the image, click once with the left hand mouse button on the file called 'Ancient.utx' and then press open. It will automatically change the look of your browser bar somewhat. All it has done is simply loaded the current images to your bar, so that you can access them easier. Keep in mind that when the editor opens, all the textures that are loaded are not for actual use in the game. They are only there for the engine itself, and so that you can see what you are doing in the editor. You should under no circumstances actually use them in a level!

    Now that we have our textures open, we can start to make something in the world. The first thing that we will make is a rough corridor in a mine. It will be where the player starts off, and I will make it fairly basic, but not the usual 'Square' corridor that so many people make, which is such a let down. Here goes...

    Now we want a corridor that is large enough for the player to stand in easily, but also to make a slightly 'cramped' feel of a mine, so here are the sizes that we will need.

    Height = 128
    Width = 96
    Breadth = 256

    These are roughly large enough for the player to stand in, but will be tight enough to make them feel pressed in. 'Ok, where do those wonderful numbers go?' I hear you ask? Great, showing signs of initiative. Cool Bananas =) Right click on the 'Cube' primitive. This button looks like this:

    Now you should see a nice little window that is open. it will have a lot of fields that you can enter information into, and you will use these fields a LOT of the time. It is much better practise to get used to entering the number manually, rather then stretching shapes and scaling brushes (All explained later).

    When you enter the number manually, you not only know the exact size of the brush, but you will be able to control it better for texturing, and that is something that is VERY important.

    This is what you should have. Enter the number in the fields as I have done, and then press the 'Build Button' to create your shape. In the windows of the editor, you will see a nice little red rectangle that has been made.

    You will use this red 'Builder Brush' to create your level with. It will never actually show up in the game. It's a little like having a template of sorts that creates your world with little stamps. You can change, shape and do just about anything with the Builder Brush, then when you are ready to use it somewhere, you will actually 'Stamp' it into the world, and only then will it actually be made into the level. If that's not quite clear yet, just keep following, it will get quite clear very soon.

    Press the 'Close' button on the 'Build a Cube' window as we no longer need it. You will still have the Builder Brush there, and that's what we will now use. Before we cut the brush from the level though, we will need to select a texture for it right? If we don't select one now, we will have to manually assign a texture to each surface, and that takes more time. For the mine corridor I am using a texture called ABasic4. It is a nice 'rocky' and 'earthy' texture that will be perfect for what we need. Now press the 'Subtract' button to actually stamp the Builder Brush into your level. The 'Subtract' button looks like this:

    The main difference that you will see is that in the 3D window, you will now have what appears to be a small corridor. Let's move the view so that it is where we can see it more clearly. Move your mouse over to the 3D window, and press the Left mouse button. It will allow you to move the screen like person running about in the world. When you hold down the Left Mouse button and move the mouse, you will be able to move forwards and backwards. You will also turn left and right. Now, hold the Right Mouse button down, and move the mouse. You will see that the view moves about. It doesn’t actually move your position though. All you are doing is moving the view about. It's like simply moving your head to see what is around you, without actually walking anywhere. Now, hold down both buttons, and move the mouse up and down. You will see that your level moves up and down. This is like basically standing in an elevator that you can control. Nice innit? Have a little play at moving about, as I won't really be going over it again for the 3D window. It's really easy to catch on, and I don't want to spend more time on it then that.

    Now that you have got the idea behind moving in the 3D window, move your viewpoint (Where you are looking FROM) so that you are looking down into the corridor from one end. It should look like this:

    Cool. You have just made your very first corridor! Now while you cannot play the level yet, you have just started out in level editing. Trust me, it goes downhill from here =) If you have gotten to this point, you should find that things get easier, and more fun as we go on. The hardest thing is to actually start out.

    Once you have started, things get more and more enjoyable, and therefore easier. Right where were we? Ah yes, the corridor. Now we could leave it like that, but it doesn’t look like a real mine corridor yet does it? (Well, not by my standards yet anyways) so let's make some alterations to it...

    The first thing that stands out to me, is the lack of supports in the corridor. I mean have you ever seen a mine without supports? Ok. For the next step we will need a small construction area. I use these all the time, as they make life so much easier, and make the level less messy, and in general better built. The idea is to simply have a nice large area somewhere in the level that is used for the creation of shapes, and other things, which will be deleted just before the level is released. This allows us to be messy while we build, and is generally a life-saver. Ok, right click the cube button again, and this time, change the numbers to 1024x1024x1024. This will create a large brush in a cube shape that we will use for our construction yard for a while (Till it runs out of room basically). Press the Build button again, then close the window.

    You should find that your screen has now got a huge cube in it. Zoom out a little in the 'Top Down' view. To do this, is almost like moving in the 3D window. In fact, all the 2D windows have the same movement in them. Left Mouse Button OR Right Mouse button will allow you to move the window around, depending on what way you move the mouse, and holding Both Buttons down will let you zoom in and out. Play with it a little to get the idea, then zoom out a little in the window, till you get something that resembles this view:

    Now to move your brush out of the way of our corridor, we hold down the CTRL key, and the Left Mouse button over the 'Top Down' view, and move our mouse left. You will see that the Builder Brush moves to the left (assuming that you move your mouse left).

    Place it somewhere off to the side. The problem here is this. What if we want to move the object further then one screen? There are a few ways about it. We can zoom out, and have more area to work with, or we can take it in little 'hops' ie move the object, then move the screen to catch up, move the object again etc etc. The problem with zooming, is that we lose the ability to see detail, which isn't too important right now, as we only have one corridor there, but it will certainly make a difference when we want to align things to exact places, and when there is a LOT of things on the screen. On the other hand, moving the Builder Brush in little 'hops' is very annoying right? What if we wanted to move it 10 screens? That would send me crazy... (Yeah, I know, not hard right? Hehee).

    There is another way to move things, but I wanted you to see that way first, as it is more often used (At least by me...). Hold down the SHIFT key, and the Left Mouse button, and drag the mouse over the Top Down view. You will notice that your brush is moving, but the window is kept in the same perspective in comparison to the brush. This is very handy. Please move the new large cube to the left of the corridor, so that there is a fair amount of gap between them. It should look something like this:

    Now press the Subtract Key again. Here is a little image in case you forgot what it looks like.

    Don't worry about textures or anything like that, as it will never be in the actual game itself, so it's not important at all. There, you will now see that we have got a nice large area to work in, and create new shapes right? Cool. Lets make a support brush for the corridor to have in it. First things first again. We will need another texture for it, so on the Browser Bar, click the 'Load' button again. This time, open a package called 'Nalicast.utx' It has got a few good wooden textures in it. The one that I have chosen for the support beams is called 'T-Wood'. It good as it is very nice, but also dirty enough and seems to be covered in dust - Perfect for a mine right? Half the fun with designing levels is to find the right textures that will make your job easier. The better a texture is, the more realistic the level looks, and that's what it's all about.

    Now create a cube that will become one of the support beams. Make it:

    Height = 128
    Width = 16
    Breadth = 32
    Now press the 'Add' button to add it into the world. This is different from the subtract button. The button looks like this:

    The reason that we are adding this time, is because the place where we want the brush to be inside has already bee subtracted from.

      Quick Lesson in Editor Workings

      The world in the editor when you first open it up, is actually a huge, great area that is SOLID. When we want to make a corridor for example, we 'Cut' it from the world. When we want to place a brush into something that we have already cut out, we have to 'Add' it back into the world. This may seem a little weird at times, but it certainly is a LOT easier then working with a world that is hollow. All we have to do is cut a corridor from the world, and technically, we could go running through there right away. It may be a little hard to grasp at first, but should become second nature by the time you finish either this tutorial, or the next one in this series.

    Great, now with that out of the way, proceed to add the brush if you haven't done so already. Cool, that will be one of the side beams that the top one will 'rest' on, and support the heavy world above it. In fact it's completely not needed, as we can do anything here, We could make a entire city stand on a toothpick, but it will add to the realism of your level, and that's what counts. Now, changing the size of your cube again, make a brush with the following sizes:

    Height = 16
    Width = 96
    Breadth = 32

    Press the Build button (It should be starting to get second nature to you shortly) and then close the window. You will see your Builder Brush is sitting there waiting to be placed. Move it so that it sits on top of the first beam like the following shot.
    Ha! I have been busted here. We have to do something else first. If you tried to move the shape about, you should have found that it will not let itself move perfectly to the place that we want it to be. This is perfectly understandable, as the grid settings are still too high. Forget the Builder Brush for a moment and Right Click in a 2D window. You will see a nice little menu appear. Move your mouse over the 'Grid' word, and you will see another submenu appear. It should look like this:

    The default setting is 16 Units, and for many things that is fine, but in this case, it doesn't allow us to have enough control. Right, now you will be able to move your support beam (Still the Builder Brush) so that it sits nicely on one end of the other beam. It should look like this:

    Pressing the 'Add' button will once again, create the Brush into something that will be left there no matter what you do with the Builder Brush (Which is what we want). Now I will show you a little trick. You could create another brush that will be the same size as your first support beam, and place it under the other side of the support right? That would be reasonably easy, but let's just 'Copy' a brush over. There are a few ways to do it, but this will be the best way. In the Front Elevation (Map XZ) window which is the Top Right, Left Click anywhere not on a brush, so that nothing is selected, then Right click the bottom support beam so that it is highlighted, and a menu appears. It should look like this :

    Highlight the 'Copy Polygons' and then left click the 'To Brush' option that appears. It will allow you to basically copy the Polygons that we made earlier into the Builder Brush without having to do anything else. When you have got the Builder Brush copied, you will notice that it has moved. It will be in the exact place as the first support beam that we made. This is normal. It copies everything to the Builder Brush, including location, which is where the brush is. Now, simply move it so that it sits nicely under the other side of the Top Support. Done that? Now 'Add' it in, like the others, so that it's there for good, and our support structure is complete.

    Now for something a little trickier then what we have done so far. We will make ONE brush out of what we have got there. In other words, rathe then creating the support structure for each place in the mine, we will create a single brush out of it, so that it does a few things. Firstly, it will actually speed the level up a little when you are playing. It is easier for the Unreal engine to create one complex brush, then to create three simple brushes. This may sound like it's going over the top, but they all add up, and let's face it, we want the fastest framerates about right?. It will also make less errors in the editor (Hopefully you will not find them, but more then likely, sooner or later, you will come across something that isn't doing what it is supposed to, and the later we can make that the better.

    Lastly, and perhaps the most importantly, it makes our life easier. The easier it is to create a level, the quicker we can do it, and the more enjoyable it is. So, trust me here (I do have a plan for all of this Tutorial) and let's create a single brush out of it. Create a new cube. This one will be 256x256x256. It's nice and simple, and certainly large enough to cover the entire size of the support structure. When you have created it, move it so that while it covers the entire structure, it doesn't overlap anywhere else (Like the edge of your original work area, if you have been building this off to the side) and then press the intersect button which looks like this :

    Now you should find that your Cube has got the shape of the entire Support Structure(Ie, there will be a red outline of the entire three beams). If it doesn't, or only has half of it, then you will find that your original Cube didn't cover the entire three brushes. If so, simply press the cube button again (It will still have the size that you last entered in memory) and do it again.

    Done that? Cool, you have just created your first Intersection, something that most people don't learn about till it's too late, and a few maps look and play very badly... Anyways, enough there. Now move your brush so that it sitting nicely inside your corridor at one of the ends.

    What? Do I hear you say that it doesn't fit??? Ha! Did you think that I would pass up this perfect opportunity to teach you another thing? (I tod you I had a plan =) for this tutorial). Yes, The brush doesn't quite fit in does it? You will find that if you align the top support beam to the top of the roof, the bottom of the Structure sticks out 16 units.

    This will be a perfect example of 'De-Intersecting'. A few moments ago, I showed you how to create a new brush out of solids that are already in the world.

    Now I will show you how to 'cut off' unwanted bits of brushes that stick out too far, or are not needed. Note that this will only work when you are adding in a brush into a place where you have already 'Subtracted' from. Ie, you are going to 'Add' the brush in. When you have it placed nicely (Like in the screen shot I have got there) simply press the 'De-Intersect' Button to cut off what we don't need. The De-Intersect button looks like this :

    It will effectively cut off the bits that we don't need for the support. Now we could simply 'Add' it into the world here and now, but that would be getting a little tiny step ahead of ourselves (and I have checked to make sure something bad will happen, and it does, so we have to do something else before our corridor is complete with Supports. Let's move our support Structure back over to the large work area that we have built.(The Large cube where we built the support in the first place).

    Add it into the world there. Depending on what way your 3D window is facing, you may see what has happened. If you see the normal support, move the window so that you are facing it from the other side. You will notice that when we 'De-Intersected' the Builder Brush, it actually made one of the sides take the same texture as the wall. This is normal, and happens to anything that we Intersect, or De-Intersect. It is simply done to make our life easier, but in this case, it happens to work against us. Ok, easily fixed though. Simply Left Click on the texture that we want to change back, and Left Click the texture that we want to replace it with in the Browser Bar. It should still be T-Wood.

    Here is a shot of what I mean.

    This isn't something that we can avoid, but on the bright side, in most cases, it certainly makes life easier for us. (Just not in this one) =)

    Now, make another Cube and intersect the new and improved Support Structure. Move it over to the corridor again, and place it to one end as before. Now, using the 'Add' Button, add it into the world, and you will see in the 3D window that it has been added, and it textured nicely like the one that we built from the three beams right? Great. Now, moving your Builder Brush to the right some more, make another support - near the middle of the Corridor. Add it in as well, and when you have done that, add in yet another one right near the other side of your corridor.

    If that is the case, great, read on, otherwise, you will have to go back a few steps and work out what has gone wrong. It shouldn't have though =)

    We are almost done with this tutorial for today (Or if you are really eager, till you load the next step) At this point, we would love to play it right? Bad News. The level isn't quite ready to be played yet.

    We need to add in some light and also tell the editor where we want the player to start when they do. First things first though, let's add some light. In the 3D window, in one end of the corridor, right-click the mouse on a wall (Near the roof) and you will see a little menu drop down.

    From the menu select 'Add Light Here' and you will see a little torch appear in it. Don't worry, this torch will not be seen in the game itself, just in the editor (Remember those textures that are loaded into the editor when you open it? The ones that I told you not to use? Well, this torch is one of those Textures. It's not something that we want to have in the game right?) Cool. That was easy. You have just added your first light to the level =)

    Now all we need is to tell the editor where we want the player to start off. In the Browser Bar, you will see a word that says (Strangely enough) 'Browse' If you drop the menu next to it, you will be able to choose four things. We are interested in one of the at the moment. It is called 'Classes'.

    When you Left Click it, it will open the Classes menu. This is where the real editing goes on. From here you can place things like PlayerStart’s (Which we are about to do) and a whole lot of special effects, not to mention all the important items that we will get to later.

    For now though, left click on the little 'Minus Sign' next to the words 'Navigation Point' and you will see a subclass open. From here left click on the 'PlayerStart' class. You should now see it highlighted. Great =) Now in the 3D window, right click in the opposite end of the corridor (Opposite from where you placed the light that is) and you should see a new option appear in the menu that opens.

      If you don't see the option there highlight the 'PlayerStart' Class again, as something didn’t quite work out, and try again. When you see it however, left-click the option, and you will see a little joystick placed into your map.

    There, almost done (I promise). There is one final thing to be done. We have to rebuild the level.

    This is where the real work goes on. Press the F8 key. You will see a new window appear. For simplicity, I will just tell you to press the 'Rebuild Geometry' Button. This is assuming that you have not made any changes to it before right? If you have then just make sure that Lighting and BSP are selected in their respective windows. If you haven't ignore that last statement.

    When it finished rebuilding (It should only take a few seconds) Choose Save (From the File Menu) and save the map as 'Phase_One.unr' (Without quotation marks of course). It's time to test the level out.

    Open Explorer, and locate the File you just saved. (It will be stored under Unreal/Maps) and double click it (Click once if you have got Win98 with Single Click Option Enabled). You will find that Unreal Opens, and your map is loaded for you to play =). Congratulations, it's your first Unreal map, and it should look pretty damn good so far...

    Here is a shot from my map:

    If you got this far, see you in the next tutorial. We will add more to the level, and I will show you more techniques, and ways of working in the editor.


Basic Room Overview

    Isolation

    You might think I am crazy but UnrealED has to be one of the easiest level editing tools I have ever used in my life. It's fast, advanced, and has a killer interface. So let us begin with a brief introduction of UnrealED. UnrealED is not the same as a Quake or Doom editor, instead UnrealED is based on "Constructive Solid Geometry." CSG is basically a huge block, and you carve out little by little out rooms. But that's not all you can do. You can also add brushes just like you would for a Quake editor. If you couldn't we would have levels with just boxes and that would be boring as hell. ;) Now that the boring stuff is out of the way let me explain how UnrealED works. Basically you have your red marker that you can shape in any way you want(rectangle, squares, spheres, cylinders, etc). With this you can subtract and add onto your map. So you cant add a brush, make it how you want it and then move on, no. Instead after you have to shape your marker, then make the room.

    You first room
    So let us begin click on the Cube. On the toolbar of the UnrealED interface. It is the 7th button down on the last row. A red box (Figure1) will appear. I call it the red marker (I don't know if there is an offical name for this :b) Now the red marker is your friend learn to love it because your whole map will be constructed by it.

    Now right click on the same Cube button on the toolbar and you will see a small menu. Click on Cube Properties ala Figure 2. Now you will see the Cube properties. Here you can choose the Height of the cube, the Width and the Breadth. Just play around with these settings and you will see how the cube changes sides (click on build for the cube to change size).

    Now make a cube 300x400x500 (heightX,widthY,breadthZ) and click on close. Before we go on I want to talk about textures. Thanks to the creative and talented artist team of Epic and Digital Extremes we have packs of kick-ass textures. So in the texture toolbar (on the right side of the screen) click on load(bottom right of the screen). Then you will see a menu of .utx files, choose any texture pack you wish to use. Be warned some of them are just for special FX and liquids. I choose SkyCity because personally it's my favorite along with NaliCastle. Now click on open. It might take a while to load. As soon as it's done go to the drop down list, on the top-right corner of the screen where it says "All" click SkyCity or whatever texture set you choose. Then on the drop down list below where it says "All" click on it and choose Walls. Thanks to UnrealED all the textures are in groups of Walls, Deco, Base, Ceilings, and more. Pick a texture you wish to use. Now back to creating your first room you should have your red marker ready from before. Click on the second button down on the last row of the toolbar. If all goes well you will see a room with a texture. There you have just took a piece of the huge cube that you can edit from.

    Moving the Grid and Objects
    Maybe this section should go before I don't know but this is how you move your grid in UnrealED.

    Right Click + Moving Mouse = Move the Grid and objects on it
    Both Buttons Clicked + Moving Mouse = Zoom in and Out

    Now when you click on an object the object's color should change. The red marker should change to a darker red then before. When you have an object selected these are the following ways to move it.

    Ctrl + Left Mouse button Clicked on Object + Moving Mouse = Move the Object
    Ctrl + Right Mouse button Clicked on Object + Moving Mouse = Rotate Object

    Moving the camera
    One of the greatest things of UnrealED is the first person window. It's so great because you can move around your map freely like if you were in the game. Here is the list of how you can move around. You have to hold down the mouse button when you want to move around.

    Left Click + Moving Mouse = Moving Forward, Left, Right, and Back
    Righ Click + Moving Mouse = Free Look, Look up, down, all around
    Both Buttons Clicked + Moving Mouse = Strafe Left and Right and move UP and Down

    It is really easy once you get the hang of it so move around your new little room, go inside, outside do what you want. It's your world.

    Texture the room
    Ok so the room looks big and boring with the same texture. So on the bottom drop down list where the texture menu is click on floor. After you do this you will see a bunch of floors textures. Click on the floor of your room and then click on the texture you want to use for the room. After that, do the same for the ceiling, but instead look in the ceiling group of the textures. Now you can use any texture you feel like using. A wall doesn't have to be a wall texture it's just a way that DE and Epic made UnrealED much easier to find the textures.

    Add the PlayerStart
    Ready to play your newly created map? Then on the texture menu you should see a drop down list where it says Textures. Click on the list and instead of pick Classes. This section has every entity you can put in Unreal (unless you create your own with Unreal Script, but that's a whole other tutorial). Now click on the dot besides NavigationPoint. The menu should get bigger. Then click on PlayerStart. You are now ready to throw in your first entity. Right click on the grid in the level and you see a menu. On the menu you see click Add PlayerStart on the top. Now you should see a joystick inside your map. That is where the player will begin the level. Make sure the player is INSIDE the room and close to the middle. I don't think the player would like being stuck to a floor.

    If you wish to play your newly created level right click on grid again and click on the button Rebuilder or just hit F8. You will see a Geometry tab before you do anything make sure Auto BSP is unchecked. Click on Rebuild Geometry, then go to the BSP tab and make sure again that Auto Light is unchecked. Now click on Rebuild BSP and you are set to go. Just hit Ctrl-P or File -> Play Level and you are on your way. If you want to learn how to put in lights keep on reading.

    Lighting your map
    One of the coolest features of the Unreal engine itself. Lighting but before you go on and start putting in nice lights that stick out lets put in a simple light. To do this right click on the grid and click on Add Light (or Ctrl-L). You should see a torch. Move the torch to the center of the map and then right click on the grid, go to Rebuilder (or just hit F8). Go to the Geometry tab and click on Rebuild Geometry, then go to the BSP tab and hit Rebuild BSP. Last but not least go on the light tab and click on Rebuild Light (or something close to that). When you are finished your room should be lighted in the first person view and you are ready to go on your first map!

    Advanced Lighting
    Nothing big here but if you want to change your light settings do this. Click on the torch and then right click it and you will see a menu. Click on Light Properties on the menu and then you will see a blue menu pop up. Go to Lighting and there you can choose the color, brightness, volume, what type of light and lighting effects. There will be a later tutorial for more advanced lighting so look out for that.

    Geometry, BSP, and Lighting
    Almost everyday in #UnrealED I mention in the topic that Rebuilding is the key to making the level. You have to keep rebuilding after making drastic changes so that it shows up in the window. It doesn't take long at all, same with BSP. Lighting also doesn't take much time to do so you would like to check out how yer level looks with the lights every once and a while. So after building your map keep on rebuilding, it will help you.


Basic Construction Phase 1

Basic Construction Phase One

    Tomasz ‘Millennium’ Jachimczak
    (tomasz@planetunreal.com)

    Forward:

    I couldn't decide where to start, so this placed itself on the screen as I sat there.

    Abstract:

    This tutorial will cover what most people need to start editing within UnReal. I will go over basic steps to carve out a sample room, adding a few of the features, make it playable (although it would actually be rather boring, with just what we will do) and give you a good start. This is a fairly long tutorial, but it goes into detail that is sadly often missed by most tutorials for beginners.

    Assumptions:

    I make no assumptions here, apart from that if you have trouble with any of the terms; you should look up my Jargon Tutorial. Things in the jargon tutorial will be marked with *.

    Tutorial:

    Introduction:

    When you have loaded UnRealED you will see four main windows, a few buttons to the left and some textures* to the right. You will also see a red cube in the lower left window. Before we go there I will however explain what the views are. The top left view is the "Top Down" or overhead view. The top right window is the "Front" view. The bottom right is the "side" view. Finally the bottom left is the 3D view. The 3D view is movable to any angle by the mouse. I will start with the absolute basics.

    View Movement:

    To move in any of 2D windows, simply press and hold a mouse button and then move the mouse. To zoom in and out, hold both mouse buttons and move the mouse up and down. The 3D window is a little more complex. Holding down the left mouse button and moving the mouse will move forward and back, and turn left and right. Holding the right mouse button will move forward and back and strafe left and right. Holding both buttons down will move you up and down, and strafe left and right.

    Making the first room:

    Right click the cube button* (This brings up a menu), and select cube properties. There you will see five places where you can type. In height field enter 256. (It should be defaulted to this anyway). In the breadth field enter 512, and 512 again in the width. Now press build (you can shortcut this by pressing the "enter" key.) In the 3d view, you should have seen a change. Now go over to the textures toolbar* and click on load. This will bring up a load box with many files in it. Select "Ancient.utx" (One of my favourites) and when the file has loaded, scroll down the textures bar until you find a good texture for a wall and select it (Single click the texture). When you are happy with the Texture, hit the "subtract" button*. You should now have brand new room in your level. Now in the 3D view, highlight the floor (single click on it) and find a good texture for the floor (try the drop down list for "floor" textures - see textures drop down lists*) and single click it. This should give you a nice Floor. Repeat the process for the ceiling (once again, you can use the "Ceiling drop down list" from the textures). You should have a good looking room now.

    Adding the light:

    We need to add some light into the room to make it playable. In the 3D view, make sure you can see a corner near the ceiling somewhere and right click it. This will produce a menu - select "Add Light Here" and you will see a flaming torch added to your room near where you clicked. (Have a look at the full lighting tutorial for more info on lights and stuff - but do that later!). Now place another light in the other corner of the room and you should just about be ready to rebuild* - but there is one more thing that is missing, a place for your player to start.

    Adding the Player Start:

    Go to where you see "textures" at the top of the screen to the right, and select "classes"* from the drop down list. This should bring up a list of things with a line in front of them. Single click on the line in front of "navigation" and you will bring up a list nested in the first one. Select "PlayerStart"* and in the 3D view, Right click on the ground where you want your player to start. Once again you will see a menu come up - this time select "Add Player Start Here". You should now see a little joystick placed on the floor of your room. Now you are almost ready to play the level, just ONE more thing.

    Rebuilding the level:

    You now have to "rebuild" the level and this is !!VERY!! important. Press F8 (this sometimes seems to do nothing - if this is the case, then go to the top menus (File, edit ect ect) and select "Options" then "Rebuild" and make sure that the "autoBSB" is checked ( I'll go into this in a later tutorial) and press rebuild.

    Saving and Playing the level:

    Well, after all of that these are a piece of cake. From the "File" menu, select save ad give it a name, then when it has finished saving, select the "file" menu again and choose "Play Level" this should now bring you into the game from a players view. That's it, You have now created your first level, Sure it not much yet, but you now have an idea on how the very basic UnRealED works. Check out my next Tutorial named "The Basics Phase Two" for more.


Basic Construction 2

Basic Construction Phase Two

    Tomasz ‘Millennium’ Jachimczak
    (tomasz@planetunreal.com)

    Well, after making my first tutorial last night, I guess that I had best make this one tonight...

    Abstract:

    This tutorial will pick up where Phase One left off. I will go into joining rooms and some more detail about the general makeup of UnReal Levels...

    Assumptions:

    I make but one assumption here, that you have read and understood Phase One - as this continues where that left off. Apart from that if you have trouble with any of the terms; you should look up my Jargon Tutorial. Things in the jargon tutorial will be marked with *.

    Introduction:

    Now that you have a basic room and a player start, you will want to make more rooms and the like for it (otherwise, lets face it it will be pretty boring...

    Adding another room:

    To create another room is just as easy as making the first. Once again, right click the "cube" button and select the height, width and depth of the cube you want to create, but this time place it just off to the side of the first room that you created, say about half the size of the room away... If haven't moved a brush* (the red wire frame of the cube) yet, it is simply done by holding down control and moving the mouse in one of the 2D veiws (on this note, make sure that the brush is selected, by clicking on it - if it's not already highlighted). When you are placing the second room, make sure that the floors are on the same height (The brush will move up and down if you have changed the height of the new cube you created) and then once again hit the "subract" button. Now that you have a second room, play with the textures - maybe continue the theme of the first or change styles enttirely (this can easily look bad though - if the change is too drastic) and place a few lights into the room.

    Joining the two rooms:

    Now that you have the two rooms, you must have a way to get from one to another. There are many different ways that this could be done - teleporters, portals and the like, but let's just use a simple hallway for now... Right click the cube, select properties, and then make it narrow (128x128 {height & Width} perhaps) and make it LONGER then will be needed to join the two rooms - but not long enough to reach the opposite side of either room. And place it between the two rooms. Now press the "deintersect" button (the one just below the subtract" button) and you should find that the hallway has been cut off where it meets the room walls. This will make sure that the hallway lines up with the walls and does not overlap anywhere (you can actually cut out something if it is bigger ie overlaps, but it slows down the game, rebuilding time, and is bad designing - lets not get into bad habits straight away eh? ). Change the textures if you need to and do the lights. Now I personally like dark hallways as they look great with just a little light spilling over from the rooms on either end - it adds to the whole atmosphere of the game, but if you want to add lights to it, the please do so...

    Adding the light:

    Now you are ready to rebuild again, and do so (if you don't you won't be able to play properly...)

    Saving and playing the level:

    Okay, now you should have a small hallway with two rooms on either end. Still not the !!BEST!! multiplayer map, but I will only teach you how to USE the tools in UnRealED, not create levels (sure that comes into it, But I won't go into floor layouts and the like - unless it is a general peice of advise...). Creating levels is up to you, You now have a basic idea of how UnrealEd works and to go into more depth (only after you have played for a while).


Æon's UnrealEd Tools Page

Welcome to the UnrealEd Tools Page
This page will tell you about the very basic things you should be aware of when editing maps with UnrealEd. Start off by reading the Navigation table, that tells you how to move in 2D and 3D views. Then there's a section on the Moving of "Objects", be they actors or brushes, an extensive section with icon shots on UnrealEd's Toolbar and finally a shortcut Keys section...

... and then... Up, up and away ! ;)

Page start: Monday, August 10th 20:28 1998
Last updated on Wednesday, September 16th 21:28 1998

UnrealEd Tools
UnrealEd...
N° | Tutorial
1 | 1st Room
2 | Light, Water & Sky

Editing
...Navigation in 2D & 3D Views
...Movement of "Objects"
...UnrealEd's Toolbar
...Shortcut Keys

Navigation

        Moving around in UnrealEd is a *lot* different from other editors I have been using these past years. Duke Nukem 3D's Build, editors for Quake II World Craft and Qoole. All movement in the 2D and 3D views of UnrealEd takes place with the help of mouse and mouse buttons.

                3D View:

Mouse Button | Movement | Result
left | left/right up/down | turn left/right move forward/back
right | left/right up/down | turn left/right look up/down
both | left/right up/down | strafe left/right strafe up/down

                2D Views:

Mouse Button | Movement | Result
left = right | left/right up/down | strafe left/right strafe "up/down"
both | (left/right) up/down | (nothing) zoom out/in

Moving and Placing of Actors & Brushes

        Another thing that I found really weird is that there is *no* movement mode tool icon in UnrealEd. And all the testing would not let me find it.

            The movement of objects is possible of course! and is done using the mouse, mouse buttons, *and* socalled qualifier keys: Shift, and Control. The Alt key is used in some special cases:

                3D View:

Qualifier | Button | Movement | Result: Move
Shift | left | left/right | grid east/west
 | right | left/right | grid north/south
 | both | up/down | grid up/down
Control | left | left/right | object east/west
 | right | left/right | object north/south
 | both | up/down | object up/down

                2D Views:

Qualifier | Button | Movement | Result
Shift | left | left/right up/down | move object/grid "left/right", "up/down"
 | right | left/right | object/grid rotate
 | both | up/down | zoom in/out
Control | left | left/right up/down | move object "left/right", "up/down"
 | right | left/right | object rotate
 | both | up/down | zoom in/out

        In other words... heeelp ;)... try testing them though :)

UnrealEd's Toolbar

        Here is a complete list of tool icons used by UnrealEd... Click on the below icons to get some more info on what these tools are for. I have grouped simular tool funtions together to make it easier to understand the basic editing possibilities UnrealEd provides.

Tool | Use | Tool | Use | Tool | Use
 | Move Camera |  | Zoom Camera |  | Add Brush To World
 | Rotate Brush |  | Sheer Brush |  | Subtract Brush from World
 | Scale Brush |  | Stretch Brush |  | Brush Intersection
 | SnapScale Brush |  | Select All Polys |  | Brush DeIntersection
 | Select All Actors |  | Select Actors Inside Brush |  | Add Special Brush
 | Select None |  | Invert Actor Selection State |  | Add Movable Brushes
 | Replace Selected Brushes |  | Replace Selected Non-brush Actors |  | Build A Cube
 | Undo Last Operation |  | Redo Operation |  | Build A Sphere
 | Pan Textures |  | Rotate Textures |  | Build A Cylinder
 | Drawing Region: Selected Actors |  | Drawing Region: Off |  | Build A Cone
 | Hide Selected Actors |  | Drawing Region: BrushZ (Depth) |  | Build A Stair
 |  |  | Toggle Vertex Snap |  | Build A Spiral Stair
 | (Get Help) |  | Camera Speed |  | Build A Curved Stair
 | Toggle Grid Mode |  | Toggle Rotation Grid |  | Build A Sheet

  ,   [image],   [image]  Camera: Normal Camera View, try holding left, right and both mouse buttons in the 2D and 3D views to get a feel for the navigation in maps. You can set the speed with the keys 1, 2, 3. Zoom Camera will speed up movement in the views. Finally there is also an icon to set the camera Speed.

  ,   [image]  World Brushes: Any primitive or more complex object you have put together has to be added to or subtracted from the World. Since Unreal uses a Constructive Solid Geometry, you will be subtracting rooms most of the time. If you then want to include a box in this room you will need to add it.

  ,   [image]  Brush De/Intersection: You can Intersect brushes, the part of the "red" active brush that overlaps with the world will be removed, and DeIntersect keep everything of the "red" brush except the overlapping part.

  Special Brush: Somehow needs to be used for building special 2D brushes, like fires etc. Add Special Brush

  ,   [image],   [image],   [image],   [image]  Brush Manipulation: Select a brush you want to manipulate and ... Rotate: Change orientation angle, Sheer: 2 opposing sides are moved relatively, thereby keeping the distance between them constant, Scale: Brush as a *whole* is scaled, Stretch: Change hight, or width, or depth, SnapScale: Scale whole brush, but do it in steps that the grid defines.

  ,   [image],   [image],   [image],   [image],   [image],   [image]  Primitives: The building blocks of everthing in Unreal. Note: you can manipulate all sorts of aspects of each primitive, by right clicking the appropriate tool icon. Thusfar provided primitives: Cube: Solid/Hollow (S/H), (h)eight, (w)idth, (b)readth, Wall Thickness, Group Name (GN). Sphere: S/H, Inner Radius (IR), Radial Sides, Vertical Stripes, GN. Cylinder: S/H, Align to Side (AtS), Outer Radius (OR), IR, Sides, GN. Cone: S/H/Capped, AtS, h, Cap Height, OR, IR, Sides, GN. Stair: Add to First Step, Step Length, Step Height, Step Width, N° of Steps, GN. Spiral Stair: Anti-/Clockwise, Fill Ex/Interior, Surface: Steps/Sloped, Ceiling: Steps/Sloped, AtS, Stair IR & OR, Wall OR, Step Height, Step Thickness, Steps per full Circle, Total Steps, Calc -> Total Height, GN. Curved Stair: Anti-/Clockwise, AtS, Step Height, IR, Step Width, Angle of Curve, N° of Steps, GN. Sheet: Orientation: Floor-Ceiling/X-Wall/Y-Wall, Size: u,v, GN.

  ,   [image],   [image],   [image],   [image]  Selection: UnrealEd provides serval mechanisms to select map parts or actors: All Polys: Selects all "walls" or "textures". All Actors: Selects all "rooms". None: Deselect all. Actors Inside Brush: Select all items, weapons, players, monsters etc. in the selected brush(es). Invert Actor Selection State: Selects all the other actors :).

  ,   [image]  Replace: This aught to be pretty powerful to be able to globally replace things. This comes in 2 flavours: Selected Brushes, and Selected Non-brush Actors: Read lights, items, monsters and the like. The latter should really be useful, e.g. give all light sources more intensity.

   Movers: Any thing that moves in Unreal map is called a Mover. E.g. doors, a drawbridge, lifts, a collapsable plank. First select one of the primitives and then the Movable Brushes tool. This changes the brushes color from red to purple.

  ,   [image]  Un/Re-Undo: Made a building mistake?... Take it back with Undo or take the Undo back with Redo 8-).

  ,   [image]  Textures: Texture alignment is *very* important!... Misaligned textures totally mess up the ambience of a map, so be sure to fix them with Pan: "Horizontally" offset the textures, or Rotate: Change the angle of the textures. As a tip try to keep as many texture "reset" and only change those very few textures that need fixing. I wonder how textures are scaled ?

  ,   [image],   [image],   [image]  Drawing Region: Maps can become very complex and make it really hard to see what you are doing... by defining different drawing regions you can selctively blend out stuff that's in the way. Selected Actors: Only draws selected stuff. Off: Draws everything again. BrushZ (Depth): Hoem ? Hide Selected Actors: Directly hides anything You selected.

   Vertex Snap: Well... I am guessing here... when manipulating vertexes ("corners") individually, this tool should force the moved vertex points to grid points. Snap... hmmm... but how the heck does one manipulate vertex points... ?

  ,   [image]  Grid Snapping: This is *one* big help for level builders. Pros don't just randomly position brushes all around. They use the grid, to ensure that all brushes are properly placed. UnrealEd provides a grid for normal othogonal Grid and Rotation Grids. The latter helps us to rotate brushed in specific angle steps.

   Help: Well this should provide extensive help on anything that has to do with the editor. Alas it does *not* work. Only the upcoming Commercial version of this editor will provide it.

Shortcut Keys

        Buttons are fine and dandy for beginners ;)... but after editing maps for hours on end you will want to have some no nonsense key shortcuts. Below you will find an complete list of shortcuts:

Special Mouse Clicking Combinations

Key | Use | Key | Use
A + right mouse click | Add actor there. | L + right mouse click | Add light there.

Keys

Key | Use
Delete | Delete selected actors.
1, 2, 3 | Slow, medium, and fast movement speed.
F1 | Help.
F4 | Actor properties.
F5 | Surface properties.
F6 | Level properties.
F7 | Compile changed scripts.
F8 | Rebuilder.
B | Toggle brush visibility in the current viewport.
H | Toggle actor visibility in the current viewport.
P | Toggle player controls or realtime update of the viewport.
Ctrl - A | Add brush to world.
- B | Load brush.
- C | Copy.
- D | Deintersect brush with world.
- E | Save as.
- L | Save level.
- O | Load level.
- P | Play level.
- N | Intersect brush with world.
- R | Redo.
- S | Subtract brush from world.
- V | Paste.
- W | Duplicate.
- X | Cut.
- Z | Undo.

Key | Use
Shift - A | Select all actors.
- B | Select all surfaces belonging to the selected surfaces' brushes.
- C | Select adjacent coplanar polygons.
- D | Duplicate selected actors.
- F | Select adjacent floors.
- G | Select surfaces belonging to the selected surfaces' groups.
- I | Select surfaces with items matching the selected surfaces' items.
- J | Select all adjacent surfaces(flood-fill).
- L | Look ahead in the current viewport (no pitch or roll).
- M | Memorize selected surfaces.
- N | Select none.
- O | Select surfaces from intersection of selected surfaces and memory.
- P | Select all surfaces.
- Q | Reverse the set of selected surfaces.
- R | Recall memorized surface selection.
- T | Select all surfaces which use the current texture.
- U | Select union of selected surfaces and memorized surfaces.
- W | Select adjacent wall surfaces (flood-fill).
- X | Select exclusive or of selected surfaces and memorized surfaces.
- Y | Select adjacent slanted surfaces.
- Z | Select none.


Æon's UnrealEd Tutorial 1 Page

Welcome to the Tutorial N° 1 - 1st Room
This is the first level editing tutorial for UnrealEd. We will build a simple room with player start, and a light, we will also learn how to apply textures. There are many other tips that will help you to get started.

Note that Semmel developed this tutorial. I only translated it into english, did my own screen shots, added some comments, and redesigned the original pages ;).

... Now rive up UnrealEd ...

Page start: Friday, August 14th 19:27 1998
Last updated on Wednesday, September 16th 21:51 1998

UnrealEd Tutorial N° 1
UnrealEd... Tools ...
N° | Tutorial
1 | 1st Room
2 | Light, Water & Sky

Tutorial N° 2
...Getting Started ... Moving about in views
...Building our 1st Room ... Step by step guide
...Final Remarks ... Always save your work!

Getting Started

        First off we will need to learn how to navigate in UnrealEd's 2D and 3D views (4 big boxes in the center of UnrealEd). As pointed out on the tools page under Navigation the mouse and the mouse buttons are used to "fly" around in the views. If you have not done so yet, please read the afore mentioned table or print it out.

As with any tutorials it is recommended to have UnrealEd running so that you can try out parts of the tutorial as they enfold. At this point, play around with the navigation in the 2D & 3D views.

Building our 1st Room

        We will now build our 1st simple room, in which we will be able to run around later on.    For this simple room we will use a cube, notice the Toolbar on the left hand side of the editor? Left-Mouse-Click on the socalled "Build a Cube" icon (on the left). A dark red wire frame cube will appear in all 4 views. You can select the cube by clicking on it in any view, it will turn red. Any manipulations you might care to do will always happen to the selected "object" you chose.

   We want to make the cube a bit bigger, for this we Right-Mouse-Click on the "Build A Cube" icon above. You should see something like the shot on the right:

Highlight "Cube Properties" and set the desired size. For this test we choose the size 256 x 256 x 256. Notice how all 4 views are updated if you happen to change the Height, Width, or Breadth. Now click on "Build" and then on "Close". You just sucessfully changed the cube's size :).

  ,   [image]  Of course this is a rather complicated and slow way to change the cube's size. You might like to try the "Scale Brush" icon (to size up the whole cube) or the "Stretch Brush" icon (to change the cube in only one direction). To do this: Hold down the qualifier key CTRL *and* the left mouse button and move the mouse to change the cube's size. (Try experimenting with these a bit and with the different views).

After this little excursion, we need to chose a Texture for our room For this notice the "bar" on the top right hand side of UnrealEd, where you can select these textures:

1 | Choose one of the Texture Package(s) that UnrealEd has thusfar cashed in Memory. Use the Load button at the bottom of the texture frame to add to this list of textures.
2 | From the current texture pack, pick a specific subgroup of textures e.g. Arch, Base, Ceiling, Deco, Door, Floor, Panel, Pillar, Switch, Trim, Wall, Window... This is a *huge* step forward... just load the texture set you want to use, and then simply pick e.g. from the floor textures available... :)))
3 | Pick one of the 3 texture sizes.

   After having picked a nice wall texture, you will have to click on the "Substract Brush from World" icon (on the right) to "place" the cube in the Unreal "world". The red wire grid cube should now be a textured square room (see the 3D view). Welcome to your first simple Unreal room :))

   We still have to tell UnrealEd where to place players. We need a socalled PlayerStart. You will find this entity under "Classes".

In the menu pick Classes, the textures will disappear and a list of classes will appear like: Brush, Damage Type, Decoration, Effects, HUD, Info, Inventory, Keypoint, Light, Menu, Navigation Point, Pawn, Projectile, Score Board, Triggers... To set the player spawn point look under "NavigationPoint / Playerstart".

   Next go to the 3D view and move where you want to "start". Place the mousepointer somewhere near the floor and then press the right mouse button. You should see something like the shot on the right.

   Pick the menu point "Add PlayerStart here" and you should see a joystick icon in all 4 views...

        So now we can play in our test room.

From time to time it's a good idea to recalculate the levels geometrie. You need to do this since UnrealEd is *beta* and has trouble updating the 4 views from time to time. To fix these display update problems hit the F8-key and click on "Rebuild Geometry" from time to time. This will open the window on the right.

Finally we want to add a light source in the middle of the map.    For this we move the mouse pointer back to the floor in the 3D view and press the right mouse button. The same menu we used to "Add PlayerStart here" will reappear, only this time select "Add Light here" and a torch icon will appear in all views. Since the torch will probably not be where you want it, you will need to to move it to the desired place in the 2D views. Zoom close to the torch, to let you see it more clearly (to do this, hold *both* mouse buttons and move the mouse in the 2D views). To move the torch around, hold the qualifiert key shift, press the left mouse button (over the torch, that will turn green) and move the mouse. Notice how the torch moves in the other views.

You should see the following on your screen:

Please take a moment and make sure you understand the 3 2D views, there is a top (xy), a front (xz), and a left (yz) view. The plane is spanned by x, y and height is z. It a good idea to play around with these views to get a feel for 3D editing. A bit of "Learning by Doing"...

Hit F8-key again to make UnrealEd calculate the lighting into the map. You should then see something like the screenshot above (3D view).

That's sorta it :)... you can play the level right away with Control + P. This will startup Unreal and autoload your map.

Final Remarks

        Let me stress it again... UnrealEd is a really nice program but it's a Beta... this means that it't still *buggy* and will frequently crash on you. So:

Save your work in regular intervalls and under differently numbered names.

This ensures that you will only lose the work of 10 minutes or so, instead of an hours or more. The history of saves will let you fall back on an older version, if you happen to have messed up your current version somehow.


Æon's UnrealEd Tutorial 2 Page

Welcome to the Tutorial N° 2 - Light, Water & Sky
Part two of the level editing tutorial for UnrealEd. After having built our 1st room in part 1, we will now change some details. First we will learn how to change some of the Light charateristics like color, intensity, type, ... Then we will add some Water to our test map... and finally a nice Sky with moons...

Note that Semmel developed the "Light" section of this tutorial, and he described the "Water" and "Sky" sections.

... Details, the details, ohh those blasted details ...

Page start: Saturday, August 15th 21:10 1998
Last updated on Wednesday, September 16th 21:56 1998

UnrealEd Tutorial N° 2
UnrealEd... Tools ...
N° | Tutorial
1 | 1st Room
2 | Light, Water & Sky

Tutorial N° 2
...Forword
...Let there be Light ... Color, Intensity, ...
...Water ... Splish Splash ;)
...May the Skys come alight !

Forword
About colored light. Before you go about editing your light sources in the next section. Please take the time to read the following notes:

Especially because it's rather easy to use colored light, don't abuse it !... it can be very annoying to have violent red, freezing blue, or jucky green lights all about. Always be aware that colors set the mood of a map... so try to cut down on too much colored light. For a few effects it's ok... You might like to use orange light in your map... and the outside parts of your map, it gives the maps a nice dawn or sunset ambience.

Appart from coloring, you can also select among many light types... for the most part I can only say... hands off! ;)... Strobes and Disco sorts of light are very darn annoying...

And please remember so save your work, regularily... I did some testing today and while adding all sorts of decoration (actors), UnrealEd messed up the map and since I only had *1* version of the map I had to start afresh... so be warned !!!... I also noted that UnrealEd likes to crash on mouse double clicks :(... *curses*...

Setting Light Properties
The light in the previous tutotial is a bit boring, so let's change the "light source" in that map. To be able to change the properties of our light source , we will need to select the torch in one of the 4 views, it will turn green.

There are several ways to change the properties of any actor. Press the "F4" key or press the right mouse button and select "Light Properties (1 selected... )", or left double click on the torch (be careful with the latter, double clicks tend to make UnrealEd crash!). The menu on the right hand side will appear, the enties that are of interest to use are marked.

To change the light properties, you only have to change the points "Light Color" and "Lighting". Click on these 2 entries to be able to edit them, you will see several new entries like in the menu shot below.

- Light Color
You can set the Brightness (values from 0 to 255 possible, 64 is the default), the Hue (coloring, default 0 is white), and Saturation (default 255, will bleach out any color to white, try lower values to see colors set with Hue).
Play around with the values, and notice how the light changes in the 3D view. For our example try the values 64, 170, 0, this will color the room in dark blue. The color update should be seen right away, if not hit "F8" to rebuild the map.

- Lightning
Now for the really "evil" stuff ;)... I'm picking out some of the points that sound interesting... Actor Shadows: Will give any "actor", be it a weapon, ammu, monsters etc. a shadow... this will look cool, but will make your map laggy, so cool it with this option ;)... Corona... Lense Flare... Light Radius: Determine the size of the light source... Light Type ... None, Steady, Pulse, Blink, Flicker, Strobe, Backdrop Light, Subtle Pulse, Texture Palette Once, Texture Palette Loop... many effects you might like to try... stick with Steady in most cases though... Volume Fog: Turn on fog... Volume Radius: Set this to something bigger than 0 to determine the extent of the fog. (Sorry just tested the above effects... hmmm fog, flare etc. don't seem to work... I am still testing it... :(...)

It might be a good idea to load some of Unreal's single player maps, and see how professionals did the job... The 3D view will look something like this:

Water ... Splish Splash =)
So after manipulating our light source we will add a small pool in our map.

Pick [image], Scale & Place it with [image] or [image], and Subtract it [image].
First we will carve out a "square" hole in the floor of the map, using a cube. . Place it in the middle of the room, and let 80% of the cube look "out" of the main rooms floor. After placing the brush you should see this in your top and front 2D views. . Before substracting our pool you might like to pick a different texture than we used for the walls. Select this texture and then hit Subtract Brush. There should be a hole in the floor now, with the new texture lining the walls.

Add Sheet [image], Scale & Place it with [image], Pick Water Texture, Special Brush "Water" plane [image].
We now want to "fill" the hole in our map's floor with water. First we need to close [image]. the hole with a "Sheet", place the sheet over the hole and scale it until it completely covers it in the top 2D view. In the "side" (front or left) views, place the sheet a bit below the main rooms foor, see the shot on the right for this.

So since be will be using the sheet as a water surface, you might like to to pick a water texture. For this Load (Button in UnrealEd on bottom left) a new texture set called GenFluid.utx, pick one water texture, by left-clicking on it.

The sheet is supposed to be something special ;)... so click on the "Add Special Brush" icon in the toolbar. You will be presented with the menu on the right.

Pick as "Predefinded:" menu point Water! Then click on "Add Special" to aply the setting and then hit "Close". At this point it's a good idea the start the rebuilder with the F8 key. As you will notice in the 3D view, our pool looks finished.

But there is still one thing left to do. We need to tell Unreal that our hole is supposed to be filled with water. For this we have to select "Classes" in the browser on the right hand side of UnrealEd, and there we click on "Info" and then on "Zone Info". Finally click on "Water Zone" (see the browser shot on the left)...

Now go into the 2D top view and right mouse click over the pool, a menu will appear that lets you "Add Info here", select it and the info icon (on right) will appear in all views. Place this icon somewhere *in* our pool. With this icon we have definded where water will be placed in Unreal, filling the hole up to the water surface.

After all this clicking around you should (hopefully ;) see something like this:


Making Rooms and Connecting Them

MAKING BASIC ROOMS AND CONNECTING THEM…

FOREWORD:

I am suspecting that you know how to start up UnrealED. Download tutorial map: Here

DIRECTIONS:

First go ahead and start up UnrealED. Now you should see something like this: (Note:The blue lines aren’t there obvioulsy, I’m just pointing out some of the button names for you.)

Well the first thing you’ll probably notice is that there’s a lot of stuff to know. Don’t worry about memorizing them all now because the more you use it the more familiar you’ll get with it. Also notice the 3D view, if you click on it and hold it you can move your view around. Hold the left button to move forwards and backwards, hold your right mouse button the look all around, and hold both and move your mouse to go up and down. Ok, now, in the bottom right hand corner where it says “Load”, click on that. Now select the texture packet called “Crypt2.utx”. Now a set of images should appear in the menu viewbar (refer to above image). Select on the texture entitled “C_FLR19”. This will be our texture for the walls. Now right-click on the create cube button and select cube properties. When in the “Build a Cube” dialog box click on solid, then put the height as 384, the width as 384, and the breath as 384. Then click “Build”. Now click on the add button. Now take a look at your 3D view, it should like something like this:

Now go to your XY view or top view above your 3D view and hold Ctrl and move the red box (known as your brush<<remember THAT) RIGHT ABOUT 128 UNITS SO IT LOOKS SIMILAR TO THIS:

Now again press the add button and you should end up with two identical squares. (Note: You use the add button when making a box outside of another, and you use the subtract button when making a box inside of another.) Now right-click on the cube properties button again and make it solid, height=256, width=256, and breadth=128, then press build and close the build a cube dialog. Now place it in between the squares and along the bottom like so:

Since the brush is in between the cubes but still not inside the cubes we are going to press the add button again. So go ahead and do that. Also this will only connect the cubes if you’ve made them exactly 128 units apart. Look below and if your map looks like that then now you’ve got a connected hallway! (Remember to move your camera around to see the inside of your level.)

Great work so far. Now we’ve got to add some lights. Look at your 3D view and rotate your camera position so that your looking at one of the box’s ceilings. Now right-click on the ceiling and select “Add Light here”. You should notice a small torch on the ceiling. Good. Now rotate your camera view and look at the other box’s ceiling and do it again. Now you’ve got lights on your level. Now is the only technical part. In the Mode Toggler click on the pull-down menu bar and select Classes. You should notice a small minus sign next to the word (*NavigationPoint). Click on that. When it turns into a plus sign you should notice that there are a lot more options to choose from. Select the option that says “*Playerstart”. Now go back to looking at your 3D view and take a look at the floor in one of the cubes. Right-click on the floor and select “Add Playerstart here”. You’ll notice a small joystick appear on the floor. Good, now go over to the other cube’s floor and do it there. Now there’s only one thing left to do and that’s to rebuild geometry. Press the F8 button and under the Geometry tab select the Rebuild Geometry button. Your level should look something like this now:

If it does then go to the file menu and save it as DM-test and then save it and then go to the file menu again and select Play Level (Make sure your Unreal or UT cd is in the CD-ROM drive.) If it does not look like the above then go back and see where you’ve made a mistake.

HOW TO EDIT TEXTURES…

Played your level and notices it was all a single texture on every wall and want to change it a bit. This is how. First in the 3D view select on any floor/ceiling/or wall and then find the texture you want while it is selected and then select that texture that you want. It will instantly change textures. Pretty easy huh?

CONCLUSION:

You now know how to make a basic level, add playerstarts, add lights, and change textures. Try experimenting with new shapes and buttons. Also open some other textures packs and check those out. This is only the first step towards level making but it’s a very good foundation to start on.

OTHER:

Questions/Comments email me at utcity@planetunreal.com

-=[ Copyright / Permissions ]=-

This tutorial is copyright (c) 1999 Matt Murphy.
This tutorial is not supported by Legend Entertainment or Epic Games.


Common "In Map" Problems

Common In-Map Problems

By Trevelyan

Presumptions:

None. This is a guide to many problems that maps have, particularly ones made by beginner/intermediate UnrealEd'ers. I have also added in a few quick notes on how to balance the map out, but I also plan on adding another tutorial based solely on that. First, there are the basic technical problems that many maps have; BSP problems, and HOM effects.

BSP HOLES:
If you are already into making maps, this is your worst nightmare. I remember my first BSP cut; I'm testing out my map as usual, and I go near a wall. The wall disappears. I die instantly for no reason. I respawn, and the wall is intact again. Welcome to the world of BSP.

What's the problem?
I won't lie to you. I have no clue as to the technical reason of what happens when BSP problems occur. What usually causes the problem though is sloppy use of brushes when placing them in your map. A big problem arises when you intersect or de-intersect a sheet when using them for zone portals. When you use the floor lofter incorrectly you will almost certainly see some kind of problem there also. When you are making hallways by subtracting brushes, and don't intersect them first, again you see this kind of thing. Basically, impatience is the cause.

What's the solution?
Be patient when creating your map. Don't go nuts with the subtract key, and make 10 hallways in 15 seconds! Always intersect 2 subtracted brushes that meet, and always de-intersect 2 added brushes that meet. When faced with an "invisible box", a solid invisible obstruction, save your map and slowly eliminate possible culprits. (Maybe that sheet? That crate? Experiment!)

HOM (Hall Of Mirrors)
This is everybody's favorite (sarcasm) effect in the whole wide world. Ever get stuck in a hole or wherever, and every frame in front of your gun remains pasted onto the last frame? (Hard to describe, but you have probably seen it before)

What's the problem?
This one's easy. When you create a map, you remember seeing that blue grid that represents space? Well if you see this nothingness in the game, this is what you see instead. If you make a surface transparent, and nothing is behind it, these will occur easily. Another cause is a mirrored surface reflecting onto the skybox. At certain angles you will get the praised HOM effect there.

What's the solution?
Not too difficult here. Just check out what you are doing more closely. You might have just highlighted the wrong special effect when selecting a surface.

PATHNODE CRASHES:
Ugh. Pathnodes are seldom fun to implement. If you get a bizarre crash when testing (or god forbid playing) your map, where the bot stands still for a short period of time before the mighty boot occurs, the solution is simply to Define your paths. Check out the tutorial if you're confused on that one.

FRAMERATE PROBLEMS:
Slow as molasses when taking on that 35-story tower? Look no further!

What's the problem?
Easy explanation: Too much stuff going on in the screen. Better explanation: Too many polygons, too fancy lighting effects and un-optimized terrain. (Especially outdoor scenery, check out that tutorial for more info)

What's the solution?
Check out some of the maps in UT for examples on how to "cover up" terrain to speed up your frame rate. In CTF-Lavagiant, for example, the mountains in-between the 2 forts not only are made up of only maybe a couple-dozen polys; it also blocks out visibility between the 2 more detailed fortresses. If that mountain weren't there, the game would have a terrible frame rate. (Not to mention suck!) Go easy on your effects too. When using fog, for example, make sure it's within a small room, out of sight of larger areas that boast a large polygon count.

LITTLE THINGS THAT ARE HARD TO ACCOMPOLISH:
Here's a list of things that seem impossible to do to a beginner at UnrealED. Other tutorials have these, but don't list the little things that make it actually work…
Creating fog: You need a zoneinfo (under classes> info> zoneinfo) within the room, and simply adjust the 3 volume settings under any light actor in that zone. The zone must have "bFog Zone" set to ON, and the magic part: You need to rebuild geometry for you to see the effect.
Screenshots: Mentioned in the screenshot tutorial also, just a reminder to get your screenshot saved well:
1-DO NOT USE MS-PAINT in any way, shape or form! The picture will come out terrible, I can assure you!
2-Rebuild geometry once you have properly imported your bitmap. Then save your work. If you don't do this, your level will be terminated! (Well, actually, the data will still be there; you just have no access to it in any way. ;) Also remember this if you are importing music, or anything for that matter.

IRRITATING TIBITS:
Here is a list of many things people hate from some mapmakers:
-Overuse of messages: Don't add 10'000 messages in a death match arena. People want to fight in an arena, not read all day. A novel effect indeed, though, but the novelty wears off sooner or later. And then you are left with 10'000 boring messages while you fight.
-Bad weapon/item placement: Don't ever, EVER place all of your power-ups in the same location. DM-Pressure is the only notable exception to this rule. Make your power-ups/weapons spaced out in order to make the players run throughout the map. Stuff like the amp and shieldbelt should be difficult to obtain.
-Half-assed maps: Stuff like texture and brush alignment are paramount in order to create a realistic environment. Never skip the little things in your map; create screenshots, add music and eye candy. Just making your idea alone and sending a "nearly finished" map to people is a bad idea. (Unless it's to something like UT City; then obviously you would want to!)

Thank you for spending your time with my tutorial. I would like to create a FAQ on this subject, but don't seem to have many people asking my any FAQ's! If you have any questions on UnrealED, E-Mail me at connors@nnex.net. Later.


COLORED LIGHTING...

FOREWORD:

In this tutorial, you will learn how to make colored lights such as that in CTF-Dreadlock and AS-Pyramid.

DIRECTIONS:

First go ahead and start up UnrealED. Now open up the Crypt2.utx texture set in the menubar. Scroll down a bit until you find a texture called 'Awall'. See below to view it:

Select the texture and then right click on the [image] cube button and click on Cube Properties. Enter the following numbers: 512 height, 1024 width, and 1024 breadth like so:

Then select on 'Build'. Now [image] add the brush to your world. You should then see something similiar to this:

Ok, now right click on the top of the cube (on the inside and in the center) and select, 'Add Light Here'. Now you should see a small torch-like thing. Right-click on that light and go into its properties. Under the 'LightColor' properties, plug in the following attributes:

  • LightBrightness - 120
  • LightHue - 250
  • LightSaturation - 75

See below for an illustrated view:

Now close the property menu and press the F8 key. Click on 'Rebuild Geometry'. You should notice your cube turn a red color. See below:

Now you have successfully created a colored light. For more information, such as changing to different colors, continue reading. Now, you're probably wondering how I knew that those three numbers above came out to be red. Well, what I use to find out certain colors is the color wheel thing or whatever it's called. Anyways, to find out different hue numbers for different colors, create a ZoneInfo in your level. Go into its properties and into the ZoneLight property. Now near the FogColor attribute you should see a black line with a little button on the right with three dots, click on it. The thing that pops up is the color menu.

Right now all your paying attention to is the colors and the little box on the bottom called Hue and a number next to it. I have decided that I want to change my color to blue. So I move my mouse onto a blue looking color and see that the hue number says 162. See below:

Now that I know what my hue is going to be I close the Color box and the zoneinfo properties. Then I again go into the Light properties and this time set the LightHue attribute to 162. Then I close the properties box and press F8 and rebuild geometry, and this is my result:

There, now you know how to change colors.

CONCLUSION:

Once again, I have started what I think an easy to make tutorial and have found it more difficult to explain, so if you have any questions email utcity@planetunreal.com. You should now know how to create colored lights and change them. Try experimenting a bit with different colors to see how they look. Also, you can do this same technique with coloring fog. If you would like to see and example of colored lighting in a level, download CTF-Dreadlock and take a look at the bases inside.

OTHER:

For comments/questions email me at utcity@planetunreal.com

-=[ Copyright / Permissions ]=-

This tutorial is copyright (c) 1999 Matt Murphy.
This tutorial is not supported by Legend Entertainment or Epic Games.


Basic Pathnoding

Basic Pathnodes

By trevelyan

-----------------------------------------------------------------------------------------------------------------------

Presumptions:
That you know how to make a map, add lighting and weapons, and don't have a clue as to how to make bots do what you want in a match.

Forward:
A quick explanation as to how the computer opponents (or "bots") work: Every bot in UT and Unreal have pre-programmed actions that are not changed. When it knows a pickup or weapon is nearby, it will usually grab it immediately. When an opponent is within sight, depending on the settings, it will attack in a pre-assigned manner. (Jumping, strafing and firing) When nothing is nearby, the bot will remain still, and actually pace around a little. What pathnodes do is force them along set paths, in straight lines.

Now on to the tutorial already!
OK, first you need to make 2 rooms; roughly 500x1000, any height. Subtract both rooms and intersect a hallway between the 2 rooms, making the hallway considerably narrow. (See the picture below) Go into your Classes window (on the right hand side) and add your playerstart actor and some lighting. Make the playerstart on the end of one room, and set a weapon or pickup (Under Inventory) behind the corner of the other room. (So if you were walking down the hall in the middle, you would have to round the corner to see it)

Now, go into your classes again, and highlight "Navigation Point> Pathnodes". (Yay!) Right click in front of the playerstart actor (the little joystick icon) and click "add pathnode". A little apple should appear (why an apple I'll never know; must be a temptation thing.)

In this case, you would want to add the pathnodes directly through the hall, meeting into the other end. The picture above is in the other end of the map, where the weapon is. Now if we ran this map with a bot right now, it wouldn't function quite right, for 2 reasons. First, we didn't define the pathways; second, the pathnodes aren't set up quite right in this picture, even with this level of simplicity.

First thing's first however. Hit F8 to bring up your rebuilder. You are probably used to just hitting "Rebuild Geometry", as it does most of the work for you. Instead, click the far right tab (Lighting) and click on "Paths Define", located near the bottom. You won't notice anything until you set up your 3D window to show pathways. Simply click on "View" in the 3D window and click on "Show Paths". You should see something like the picture below:

(Notice the blue and red lines? Those are your paths.)

Now everything is set but one thing. It's better to learn the right way first-hand, so observe closely. The pathnode in the hallway is not in visible range of the rocket launcher. The bot in this map will run to the pathnode in the center of the next room, and then run backwards to the rocket launcher.

No real biggie at first, but a quality map must have bots that behave like humans. Humans don't run back and forth like this one will, so a little more work is needed here. (Most likely you did it right the first time around, but this example will help you anyway)

Now, all that is needed here is the pathnode near the center of the above picture to be moved ahead slightly. Re-define the paths using the Rebuilder. This will create another path, having the bot run straight for the rocket launcher, and then proceeding to the center…

(Hooray! Now it's properly set up! (In actuality, another pathnode closer to the weapon would be a good idea, especially if you are to place ammo there.))

It's an annoying but good practice to "run through" the pathnodes in the 3D window, making sure each one is within sight of another, and is easy to get to. Always make sure that pathnodes form a pattern through a map, and never use weapons as paths themselves; if the weapon is gone for some reason, the path is cut, and another path should be available to it. Also note pathnodes do not work for elevators, but do work for teleporters. Elevators require a Liftexit and Liftcenter, which will be covered later on. It is most important to not overdo pathnodes; you usually don't need multiple paths is say, a hallway or small room. As mentioned earlier, when confronted the bots will move off of these paths and attack, usually furiously. The purpose of them is to guide them through when they are "mellowed out", for the most part. In other words, KEEP IT SIMPLE!

Any questions or comments E-Mail me at connors@nnex.net. I would be glad to help out. Until then, happy frags!


Advanced Pathnoding

Topics:

  • Pathing movers such as elevators
  • Making bots take different paths in Capture the Flag maps
  • How to make bots use portals correctly
  • How to create teleporters correctly
  • How to make Jump spots
  • How to use Kickers
  • Making bots snipe and defend correctly

Foreword:

I am suspecting that you have some sort of level ready and are familiar with UnrealED 1.0 or UnrealED 2.0.

Directions:

Pathing movers such as elevators:

Ok, lets start out by you already having a mover ready. For this section I will be teaching on pathnoding bots to use movers, so an elevator would be the best choice to pick. Now open up your Classes Broswer. Go into the Navigation objects. Then look for the following two objects: "LiftExit" and "LiftCenter". See below for visual:

Ok, now click on the LiftExit object and go into your 3D view. Place the LiftExit where you want the bot to get on and off. One will be at the top, and one at the bottom. Make sure it is fairly far but not to far from you elevator/mover. Make sure you place two of those. Ok, now go back to your Classes browser and select on LiftCenter. Now carefully place your LiftCenter right over the center of your lift. See below for an example.

Once you have gotten all these set up, it should have a similiar look to this:

Ok, now it's time to get it set up. First go into your mover's properties. Set the tag to something unique and remember it. Now go into the Object settings and under InitialState, choose StandOpenTimed. This makes the mover go when a person/bot stands on it. See below for visual:

Now close the mover properties and open up the LiftCenter properties. This time go into the LiftCenter settings (found in the LiftCenter properties box) and next to LiftTag, put it as the tag you used for your mover. See below for visual:

Now go ahead and close the LiftCenter properties and select on both LiftExit object in the 3D view by holding the Ctrl button down to select the second one. Right click on one of them and go into their properties. Under the LiftExit settings (found in the LiftExit properties box), go to where it says LiftTag and set that again to whatever it was you set your mover's tag to. Again, see below for visual:

Now close the LiftExit's properties box and deselect them. Now go ahead and redefine paths. To do this in UnrealED 1.0, press the F8 key and go to the last tab and select on Paths Define. To do this in UnrealED 2.0, press on this button [image], which is the Compute Paths button. When that is finished, go to your 3D view again and right click on the gray bar just above it. Then go into View and select on Show Paths. Visual below:

You should now see two or three blue lines, one connecting the bottom LiftExit to the LiftCenter and one connecting the top LiftExit to the LiftCenter. This is good. If you do not see them, you probably tagged them wrong. See below for an example of a correctly done one:

Ok cool, your down. Now just rebuild gemometry, or if in UnrealED 2.0 rebuild geometry and lighting and test it out to see if it works. Good luck!

Making bots take different paths in Capture the Flag maps:

Now this one took me some time to really figure out, but I finally got it. Anyways here is how to do it. First open up your Capture the Flag map or whatever map you want to try it on (CTF highly recommended) and open up your classes broswer. Now before you select anything in it, go to the midpoint of one of your paths in your CTF level. Making sure that you are near or are at the midpoint, remove some of the pathnodes in it. Now you should have a gap in your path. Ok, now go back to your classes broswer and go into NavigationPoint. Then select on the AlternatePath object. Now place one on one side of your midpoint gap and the other on the other side. Basically what you should have now is a path that has two alternate paths connecting it in the middle. Now if you want you can add a pathnode directly in the center (never figured out if it was neccassary but it might be so put one in anyways). See below for a visual of what I just attempted to explain:

Once you have that down, do it for every path in your CTF level. Once you are down with that, assign half of the AlternatePaths their team number. To do this, first select on the AlternatePath that is closer to the blue base (This would be team 1), now select go into its properties. Under AlternatePath, set the Team attribute to 1. See below for visual:

Now do this for each set of AlternatePaths in your map. Remember to only assign the Team attribute to the AlternatePaths that are closer to he blue base. Also note: If you assigned the red base as team 1, then choose the AlternatePaths that are closer to the red base and set their attributes to Team 1. When you are finished with all of that, redefine you paths and rebuild geometry and lighting and test it out. Good luck!

How to make bots use portals correctly:

Before I begin with this section I would like to clarify that you have already made your portals and have placed the WarpZoneInfo's into the portals themselves. In this section, I will only be teaching you how to pathnode them correctly for bots.

Basically all you need to do is put the pathnode in front of the WarpZoneInfo on both sides of the portal like so:

<--This is one end
<--This is the other end

Well I think that's it. After you've done that just go ahead and rebuild geometry, lighting, and paths and you'll be all set. Good luck!

How to create teleporters correctly:

For this section I will be focusing on pathing visible teleporters, but this can also be done the same way as normal ones can. First start out by placing the teleporters where you want them. Next select on one of them making it turn greenish. Now you should see a small red arrow pointing in some direction. Hold down the right mouse button and the Ctrl key to rotate it. This red arrow is pointing to the direction that the bot/player will exit out. Position the teleporters' arrows to allow the player a good view of the battle when he comes out of the teleporter. This is about a five second process but has been known to be done in two seconds :) Anyways, that's how to do it.

How to make jump spots:

Coming Later

How to use kickers:

Coming Later

Making bots snipe and defend correctly:

A question that arises a lot in the UnrealED forums is how to make bots snipe in custom maps. Well, I think it's time to end it with a written tutorial :) First, open up your level and go into the classes browser. Go into NavigationPoint, then into AmbushPoint, and select on DefensePoint. See visual below:

Now place the defensepoints in the areas in which you want the bots to defend or snipe. Since we are talking about how to make bots snipe, I will explain this first. First click on the DefensePoint and right click on it. Go into its properites. Then select on the plus sign next to AmbushPoint and set the bSniping option to True. Then click on the plus sign next to DefensePoint and set the Team number this defensepoint will be for. I am setting this defensepoint up for team blue which is team 1, so I put 1 next to Team. If setting this for the red team, leave it at 0. (Note: If the bots don't use the right defensepoints in the game try switching the numbers around.) Now next to Priority, set it to a number. I am setting mine to 101. Let me explain why this is important.

Let's say that you have three DefensePoints for each team in your CTF map. One you put down as a 50 priority, one as a 51 priority, and one as a 52 priority. Then you start up you level and assign two bots to defend the base. The first bot will go to the defensepoint that has the highest priority number. The other bot will go to the next one down unless there's a second DefensePoint with the same number. In this case the two bots would be covering the DefensePoints with the priorities of 52 and 51.

Ok, now you should understand the concept of the priorities in DefensePoints so I'll be moving on. For an example of how I configured my DefensePoint, look below:

Ok, the next and last part is to position the arrows within the DefensePoint, so that the bots will know where to look. Click on the DefensePoint in your map, you'll notice a small red arrow in your 2D views pointing somewhere. This arrow is showing where the bot is going to look. If you have made you DefensePoint in a tower (like mine), and want it to be looking down at the ground below, just move the red arrow so that it is looking in that direction. To do this, hold the Ctrl key and the right mouse button and drag it around. Do this for every DefensePoint in your map. Below is an example of how one of my DefensePoints looks.

Conclusion:

You should now be familiar with pathing unique things for bots, such as evevators. Most of these techniques take time to master so don't fret if you can't do it the first time correctly.

Other:

-=[Copyright/Permissions]=-

Questions/Comments email me at Murphy149h@mediaone.net .

This tutorial is Copyright (c) 2000 Matt Murphy.

This tutorial is not supported by Legend Entertainment or Epic Games.


HOW TO CREATE SKYBOXES…

FOREWORD:

I am suspecting that you know how to Create a basic level, using lights, Rotating brushes, and knowing the basic buttons, and are familiar with the sheet properties. Download Tutorial Map

DIRECTIONS:

Right now we’re going to create a simple but quality skybox. Look below to see it:

2-4

Start out by opening or creating a level. Next open the texture set titled: ShaneSky.utx. There are other texture sets for skyboxes but for now I’m going to use this one. Now, off to the side somewhere, so that’s it’s not touching anything create a 1024x1024x1024 cube. Use the STARS texture and add it to your world. Now create a 256 length, 256 width, and 128 height cube. Move it to the bottom center of your skybox cube and select the PANTILEB texture and subtract it. The add a light on the top of that cube and if you want press the F8 key and rebuild geometry to see what it looks like. Next click on the sheet button and right click on it and go into sheet properties. In sheet properties choose the Y-Wall orientation and under Size put: U:256, V:128, then click build. Now align the brush so that it is on the side of the box and is on top. Refer to figure 2-4a:

2-4a

If it your screen looks the same then continue otherwise repeat the above process and try again. Now click on the Add Special Brush button (Big green one with dotted lined square in it.). We want to build a Masked Wall so go ahead and select that from the Predefined options. Now under Masking choose Masked, under Visibility choose 2-Sided, under effects make sure nothing is checked, and under Solidity choose Semi-solid. Then click on the PANTILE1 texture and select Add Special. You should notice something like this:

2-4b

Note: PANTILE1 and PANTILE4 seem to line up well when creating skyboxes so I recommend using that pair. Now either rotate the sheet or go back to sheet properties and choose X-Wall. Repeat this process all the way around until you get to something like this:

2-4c

(Note: Sometimes you won’t be able to see the masked wall at first. So to see it press the F8 key and rebuild geometry. If you still cannot see the masked wall then try again and make sure that it is 2 sided. Also if you notice that the textures aren’t aligning correctly try rotating the object and adding a special brush again and/or try selecting the masked wall and changing textures. Once aligned they should look alike like the above image.) Now that the hard part is over with all you have to do is go into Classes and click on the minus sign next to *Info, then the minus sign next to *Zoneinfo, then select *SkyZoneInfo (not the minus sign). Then right click in the middle of your skybox in your 3D view and select Add SkyZoneInfo here. Finally go back to textures, select STARS again, and create a sheet with the following properties: Ceiling/Floor orientation, U:1024, V:1024. Then click on Build. Now align the Sheet to be centered on top of the skybox and just 16 – 32 units higher than the masked walls. It should look like this:

2-4d

If I does then click on Add Special Brush and create another masked wall the same way you’ve done before. Now you should see a semi see through set of stars. Good. Now go back to your level part and select any walls/ceilings that you want your to view your skybox as a background. Right click on the selected texture(s) and go to Surface Properties. Now under the Effects tab select Unlit and Fake Backdrop. The close it and rebuild geomoetry and check out your cool spacelike skybox!

----------------------------------------------------------

ADDING OTHER EFFECTS TO YOUR SKYBOX…

So you want to add more stuff to your skybox eh? Well below are some of the effects you can add:

-Planets
-Lightning
-Rain
-Movement (Like in Facing Worlds)

(Note: These are only the effects that I know of, there may be more.)

Adding Planets is fairly simple, all you have to do is create a sheet that is aligned with the floor and ceiling at about 128x128 – 256x256 units and make it masked and double sided and you’ve got a planet. Just put it below your larger set of stars under your 1024x1024 STAR sheet and you’ve got a planet. (Note: Planet textures vary in size which is why I said 128x128 – 256x256 units because these are the average sizes.) As for lightning, rain, and movement, I’ll have to cover those in a future tutorial.

CONCLUSION:

You should now know how to make a regular skybox. Try using different texture sets such as Skybox.utx and make some different textures skyboxes to fit your maps atmosphere. Also another trick is making all the masked wall on the floor of the original 1024x1024x1024 without putting them on top of the 128x256x256. For more information on this download one of my maps: DM-Sanddunes or CTF-BunkerCommand and see the skybox for yourself.

OTHER:

Comments/Questions Email me at utcity@planetunreal.com

-=[ Copyright / Permissions ]=-

This tutorial is copyright (c) 1999 Matt Murphy.
This tutorial is not supported by Legend Entertainment or Epic Games.


Adding Screenshots to Your Level

FOREWORD:

I am suspecting that you have some sort of level ready.

DIRECTIONS:

Start up the Unreal Tournament. Once you've done that go to Player Setup and choose play as spectator. Once you've got that settled start a practice session and choose either 1 or 0 bots. Now start the game and fly around until you find a good spot for a picture. Once there press the F9 key. Now close Unreal Tournament and start up UnrealED. Also start up an image program you have such as Paint Shop Pro, Adobe Photoshop, etc, (NOT MsPaint!). Now, in your image program create a new image that's 256x256 and 72 resolution. Now open up the most recent screenshot you've taken in your UnrealTournament/System directory (Files are named "Shot0001.bmp" for example). Choose the one with the highest number because that's your most recent one. Open that file and copy it to your new file. Now it's going to be larger than 256x256 so either scale it down or resize it in MsPaint. Once you've done that save it as "screenshot.bmp" (or "screenshot.pcx")<--(Without the quotes). Save it in your UnrealTournament/Textures directory. I prefer saving it as a bmp file because it looks better on my computer. Now go to UnrealED and click on the import button on the bottom right side of the screen. Open up your file in the textures directory. Now in the name box put "screenshot" (without the quotes), in the group put "None" (without the quotes), in the package box put "MyLevel" (without the quotes) and uncheck Generate Mipmaps in the Properties section. Then press Ok. Now in the texture viewbar you should see a smaller version of your screenshot. Good. Now press the F6 button to bring up the level properties dialog. Go into LevelInfo, then select your screenshot on the right and then where it says Screenshot as one of the LevelInfo properties, select it. Then press use and it should say something like this: Texture'MyLevel.Screenshot' . If it does then save your map and test it out in the pracitice session dialog in the game to see if it's there.

CONCLUSION:

You should now be able to make a screenshot image for your level.

OTHER:

Questions/Comments email me at utcity@planetunreal.com

-=[ Copyright / Permissions ]=-

This tutorial is copyright (c) 1999 Matt Murphy.
This tutorial is not supported by Legend Entertainment or Epic Games.


Adding Location Strings

ADDING LOCATION STRINGS TO YOUR LEVEL...

FOREWORD:

I am suspecting that you know how to set up zones using sheets and you know where to find zoneinfo's and add them to you level correctly.

DIRECTIONS:

Ever wonder how Epic added those little location identifiers that showed up when one of your teammates talk to you? Well here's how to make them. First, using a level you already have. Set up your zones using the sheet tool. Next add a ZoneInfo object into each zone. The ZoneInfo object can be found in Classes>>Info>>ZoneInfo. Now for each ZoneInfo you have go into their properties dialog and then click on the plus sign next to the attribute section entitled 'LocationStrings'. Now under ZoneName put the name of the area/zone that you want to call it. For example look below:

CONCLUSION:

Now you know how to add location strings into your level.

OTHER:

Questions/Comments email me at utcity@planetunreal.com

-=[ Copyright / Permissions ]=-

This tutorial is copyright (c) 1999 Matt Murphy.
This tutorial is not supported by Legend Entertainment or Epic Games.


The Art of Balance

By Trevelyan

Presumptions: That you are at least an intermediate at UnrealED. This is merely a helpful tutorial on how to make your map as balanced as possible. I will also explain which weapons are best for whatever situation.

First off, let's look at the weapons and powerups that are placed within your map:

Chainsaw:

It is seldom that you see a chainsaw in a deathmatch arena; if you are in the heat of a deathmatch and you decide that a melee weapon is better than a rocket launcher, then have fun dying. In other words no one wants a chainsaw, so don't make it a crucial pickup in your map.

Enforcers:

Again, not a great pickup to have within your map either. Note that adding a "double enforcer" does nothing when added to a map; simply adding an "enforcer" will allow a double-enforcer to be used. A personal preference is to occasionally add an enforcer directly on a playerstart, giving the player more of an edge against spawn-killers.

GES-Biorifle:

This weapon is suitable for small corridors and the like. Also consider placing the goo-gun in high altitudes, allowing you to shoot downward at your opponents. Ammo for this gun should usually be close by, as running around for ammo for it tends to be a waste of time.

AMSD Shockrifle: One of the 3 weapons that must be spaced out between them to allow a balanced map, this weapon needs to be placed very carefully. Too much ammo for this weapon will force your map to become a "shock arena", and take away from it in time. Use Epic's levels as an example of good AMSD placement. In Liandri for example, there is only one AMSD, and collecting ammo for it requires a tricky jump, or wasted time climbing up that ramp. A good setup for placing this gun usually has the AMSD with one shock core, with other shock cores in other spots in the map.

Plasma Rifle: This gun is most suitable for wide-open areas, although the gun can be placed pretty much anywhere for a desirable effect. Ammo should be somewhat plentiful, even if running out is actually pretty rare.

Ripper: Definitely place this thing in a place that allows the gun to do its best work: Bouncing around corners. Try not to have the players attempt to defend themselves in wide-open spaces with only the ripper. (It can be done, but it's not a fun situation ;)

Minigun: Personally, I loathe this gun due to a constant 300 ping. However, whenever a "good marksman" is ripping me to shreds (read: LPB) I notice it's in rooms that are rather large, but not wide-open. It's really a weapon that can be placed anywhere, however, so not much else can be said on it.

Flak Cannon: Another of the 3 weapons that must be spaced out between them. The flak cannon is rather versatile in the hands of a good player, so placing it wherever will work fine. Just make sure that 1: Ammo is tough to find for it, and 2: Getting the gun itself is somewhat difficult.

Rocket Launcher: The last of the 3 weapons to space out. It's definitely the most versatile weapon in the game, so place it in an easy to reach location that attracts attention. Ammo must be very sparse on this one; you should have to walk through the whole map just to load up on ammo.

Sniper Rifle: Place this in a corner of the map that allows you to camp while firing at unwary opponents from afar. It sounds cheesy, but this attracts anti-campers to the same location, creating a nice level of complete havoc.

The Redeemer: I would classify this one as a power-up rather than a weapon. Remember that your map can be ruined with just wrong placement of the Redeemer, and that you don't have to place it in your map at all if it makes it unbalanced.

ShieldBelt: Never, ever have more than 1 shieldbelt in a map, unless it's a CTF perhaps. Make it a challenge to acquire if possible. Deck16 makes getting the shieldbelt a real hassle in a crowded match for example.

Damage Amplifier: This thing is widely considered to be cheap. And any map that doesn't have it in there will not be considered a bad thing. If you do place it in your map, make it extremely difficult to acquire! A good idea would be to make it accessible only by a piston-jump (like in Liandri, Malevolence and Turbine) so that the bearer at least will take damage for his covetousness.

Invisibility: In Unreal this pickup was beyond the realm of cheap. In UT this pickup is not very useful. In fact, in team deathmatches it causes team kills and "pushes"! Therefore, don't go nuts trying to perfect the placement of this one.

Jump Boots: Not always a necessary addition to your map; really, it's all up to the mapper as to weather or not they want boots in. The primary purpose of the boots are to make a map more accessible if need be.

Keg O' Health: Not much to say here except that you should place this away from any other health pickups; and of course not to place it too close to any other powerups.

Body armor and Kneepads: Space these armors away from other powerups, and try to get both armors to be as far apart as your map can handle. Do not put more than 1 of either armor in your map either, unless your doing a CTF map.

Health vial and Health Packs: Don't overdo either of these. Health vials are good for being near spawn locations, as it's a bit of a boost. Health Packs should be in pairs of 2, and spaced out throughout the level. Don't let an injured player heal up quickly due to bad health placement.

Other notes on balance:

Connectivity: Your map should have a "flow" to it. Many people dislike the use of doors in a deathmatch arena; do to the fact that it interferes with the players running through the map. (Also, doors are a pain when you have a high ping, due to the lag time of them opening.) If you do have doors within your map, try to use triggers to open the doors, grocery store style. There should be a flow through the map that makes you come in contact with most of the weapons, and come in contact with your opponents. Never put dead-ends in your map, and try not to make too many deadly pitfalls either. Lastly, depending on your map (since it's purely a matter of taste) you might want to create teleporters that allow you to run through the map easier, instead of backtracking.

Spacing out your weapons/powerups: Do not ever place the most powerful weapons (AMSD, Flak Cannon, Rocket Launcher) within close proximity! You want even the most experienced player to run throughout your map in order to kick some @ss. The 2 most crucial elements are the Flak and RL being spaced far apart, and the Amp and Shieldbelt being spaced apart. And try not to place too much ammo either.

Secrets: Secrets are fun to create; but much care must be taken when implementing them. Remember that most players eventually know all secrets in a good map; so the secret's contents mustn't be too rewarding. (For example, don't make a secret contain a shieldbelt, amp and mega health in one spot.)

Playerstarts: Personally, I generally place precisely 16 playerstarts in a map to avoid telefrags at the start of a crowded match. However, some maps are very small and require less. Remember that most maps never have 16+ players going at once, and too many playerstart actors can cause annoying misbalances. Don't place playerstarts in midair, no matter how cool it is. The reason is that if you hold the crouch or walk key when you spawn in midair, you will remain there until you let go! The same applies for teleporters also. Lastly, Make sure that there is a weapon near every playerstart. There are few things more frustrating than spawning and looking around all day for a weapon.

I do believe this covers most of the bases on balancing out your map. I may be wrong on some subjects due to a matter of taste, or that a special design on a map would permit something that normally wouldn't. I do, however, hope that this guide will help you out.

---Until next time---

Trevelyan


How to Create Teams

HOW TO CREATE TEAMS IN UNREALED...

FOREWORD:

I am suspecting that you know how to create a simple CTF level with lights, flags, and pathnodes.

DIRECTIONS:

First create a level or use one that you already have made. Now in the Browse Drop Down Menu Bar on the top right area of your screen select "Classes". Now look for a phrase called "*NavigationPoint", you should see a small minus sign next to it. Click on it. A drop down menu should appear before you. Now with you cursor select "*Playerstart" (Note: If a blue box appears close it.). Now in your 3D view right click where you want to place the player to start. Then select "Add Playerstart here". You should notice a small item like a handlebar appearing on the place where you selected, if not then try again. Then go to a new area and place one there (Of course I reccommend doing more than one playerstart in a real map but for this tutorial we are just using one to two each. Once your playerstarts are in place go ahead and select all the player starts (If more than one you are selecting-hold the CTRL key while you select each one.) that you want for team 1 (Red Team). After all are selected right click on one of the playerstarts and choose Playerstart Properties. Now you should see a new box with the words Playerstart Properties on it. Good. Next to where it says Playerstart, click on the + sign. A pop down menu should appear. Now look where it says TeamNumber, on the right of it you should see the number 0. Click on that 0 and delete it and put 1. Now close the box up. You will NOT have to do the other team because the default is always set to team 0 which we want any ways. Now go ahead and rebuild and test it out. Make sure that you've placed flags and pathnodes for the bots to both flags or the teams won't work correctly. You'll get both teams starting in scattered areas and not in their correct zones. If you get one team of red in one area and a team of blue in another then you've done it correctly.

Note: The following TeamNumber attributes determine the color of the team.
Blue Team: 0
Red Team: 1

CONCLUSION:

Teams can be used in Domination, Assault, and Capture the Flag modes only. In the advanced tutorials I will teach you how to make secondary team playerstarts.

OTHER:

For comments/questions email me at utcity@planetunreal.com

-=[ Copyright / Permissions ]=-

This tutorial is copyright (c) 1999 Matt Murphy.
This tutorial is not supported by Legend Entertainment or Epic Games.


Creating Realistic Outdoor Scenes

By Matt Murphy

FOREWORD:

Ever wonder how Epic can make those walls, cliffs, terrain, and just about everything else outside look so real? Well after a little experimentation I've found a way that really works. Basically what I did was I created a grayscale image, then imported it into Terra Edit using that I imported into UnrealED. Right now I'll show you how to do this, but first you will need UnrealED of course, Terra Edit, and a paint program. I will be explaining on how to make the grayscale images using Adobe Photoshop but this is possible in Paint Shop Pro. I just find it easier in Adobe Photoshop. Ok, now onto the tutorial.

DIRECTIONS:

Part 1: Creating the Grayscale

First open up Adobe Photoshop, or whatever paint program your using, and create a new image that is 512x512 and that has a black background. Next set the Red, Green, and Blue values to 64, 64, and 64 for the foreground color. To do this using Adobe Photoshop look below:

You should end up with a gray color. Ok the next part is freehand so select either the [image] Paintbrush tool, or the [image] Airbrush tool. Now, in the same little box where you selected your color, click on the brush tab and select the little box that has a circle in it with the number 45 as in below:

Now on your image start painting some random lines or create a little scene. Note: The lighter the gray color is the higher in elevation it will be in UnrealED so black is sea level and white is mega high or something (never used white before). Anyways when you have something similiar to below you can continue.

Now change your foreground color again to the following values: R-16, G-16, and B-16 (Red 16, Green 16, and Blue 16), and also change your brush size smaller to about 35. Now you'll notice that you have a darker gray color. This will be terrain just above sea level while the black color will be at sea level. Now again draw on your image but this time in between the lines like so:

Now expand the image and use the [image] Selection tool and select the entire image. Then create a new image that is 256 by 256 and has a white or colored background. Now paste your previous image onto the new image. Then go to the Edit menu, down to Transform, and select on Scale. Then resize the image to 256 by 256. Now again maximize your new image and use the [image] Selection tool and select the entire image. Create yet another new image that is 256 x 256 but this time make it a grayscale image. See below for illustration.

Copy the old image onto your new image. Now go to the file menu and select Save a Copy. Save it as a .BMP file. Then select the following properties and click on Ok.

Part 2: Importing it into TerraEdit

Now go ahead and close Photoshop if you want and open up TerraEdit. Go to the file menu and select Import. Then open your file and you should see a small little box appear asking you for a grid scale and size. Set it to the following: Grid Scale:64;Grid Size:32x32. See below for illustration:

Then press ok and you should see your terrain appear. This is how the one I made turned out:

Now if you wanted to make any revisions either go back and modify that grayscale or create a new one or you can modify it in TerraEdit. Use the directional keys to move the red arrow around. Press the Enter key to move the point a unit up and press the Shift key to move a unit down. You can also smoothen out your terrain easily by going to the Tools menu, then into Filter, and select Smooth. Then go to the File menu and choose Export. Save it as anything you want but just remember where you saved it. And make sure you chose Export and not Save!

Part 3: Importing it into UnrealED

Now go ahead and close TerraEdit and open up UnrealED. Now create a cube that is 8192 x 8192 x 8192. Next open up the GenEarth.utx texture file and find the texture called 'Dirt1'. Click on that texture and add the cube into the world. Now go to the Brush menu and select Import. Now find the file you saved from TerraEdit and click Ok. You should then see a box with some options on it. Click on the following ones.

When using UnrealED 1.0 | When using UnrealED 2.0

Now you'll notice that the brush is only 1/8 the size of the cube in area. This can be fixed. Right click on the brush and go into its properties box. Then click on the plus sign next to the word Brush, then go into MainScale, then Scale, and set the X, Y, and Z values all to 4. See below for illustration.

Now align the brush to the bottom of the cube and so that all four sides of the brush are inline with all four sides of the cubes. Once aligned correctly select the texture called 'MGrass7' from the GenEarth.utx group and subtract the brush. Excellent, now you've got your outdoor floors. Use the camera to take a look around your terrain. From there you can add lighting, trees, bases, bushes, water/lava, or just about anything to make it look more realistic. I will be explaining these in a later tutorial so for now you are on your own, but I don't think its to hard to figure out. To make realistic walls, all you need to do is a a very light gray, almost white color to the outside perimeter of your grayscale and it will be as if you are in a valley. Below are some examples of grayscales.

CONCLUSION:

Now you should know the basics of creating realistic outdoor landscapes. Future tutorials will include such things as rocks, river systems, cliffs, lighting, and more. Realistic mapping takes time to master so don't expect to be an expert at it soon. This tutorial is just made to point you in the right path.

OTHER:

-=[Copyright/Permissions]=-

Questions/Comments email me at Murphy149h@mediaone.net .

This tutorial is Copyright (c) 2000 Matt Murphy.

This tutorial is not supported by Legend Entertainment or Epic Games.


Wolf's Basic Tutorial- A Beginners Guide to UnrealEd

Introduction

This is a beginners tutorial. If you are experienced with UnrealEd you will probably be bored with this tutorial, but go ahead and read it anyway. Who knows, you may learn something!
The purpose of this tutorial is to show you the basics of getting started with UnrealEd. In it you learn to make basic rooms and add detail to them, how to do basic lighting, texturing, add actors, etc...We will build a small level while familiarizing yourself with the UnrealEd interface.
This tutorial should be enough to get you started. Subsequent tutorials will go into more detail on certain aspects of using UnrealEd.

Getting Started

First of all, to get the most out of Unreal, make sure you have the latest patch (currently 225f) available at Unreal.com.
Second, if you have any problems starting the editor then get the latest UnrealEd fix here.
Third, To get the best performance out of the editor, limit what you have running in the background. Remove everything unnecessary including the screensaver to get maximum performance from Unreal and the editor. UnrealEd is a memory hog.
Tip: If you have been running a lot of games or other CPU resource sensitive applications, then reboot your system. This will help clear virtual memory and RAM and will allow maximum performance from UnrealEd. The more memory you have free, the less chance of a "random crash."
Last, If you have been a Quake/Quake2 editor and are trying out UnrealEd then it might be helpful to read this article: Making the Change.

The Interface

Okay, you got the latest patch, you have the UnrealEd fix and you can run the editor now. This is what you should see:

If the 2D view windows are black then reset all camera views from the menu bar at the top of the editor. This should fix things up nicely. In this (and all of my other) tutorials, I will refer to the icons and other parts of UnrealEd by the names in the Tool Bar Reference. Familiarize yourself with this interface.
You can customize the look of your editor by changing the position of certain windows, etc... but I will always assume they are in the default position when referring to a location.

After you have completed the basic tutorials, feel free to customize UnrealEd as you see fit, but for this tutorial it will be much easier to follow along if you are using the default positions.

Take some time to study the interface. Check out the Browser and right-click the Tool Bar Icons to get familiar with their names.

Features

UnrealEd combines several tools into one all-purpose game editor. It has a Level Designer, Script Compiler, Texture Browser, Texture Making, Package viewer/maker, and Mesh viewer all rolled into one.
Needless to say it's a very powerful tool. We will be mostly using the Tool Bar, and Browser, and of course the 4 map view windows. Know where these are.
Tips: As I tell you "the long way" to do something, I will also mention the "short cut." It's a good idea to refer to the keyboard command and short cuts as we go along.
Click here for the Keyboard Shortcuts.

Menu Bar

Most functions can be accessed via the Menu Bar. It should be evident what they do, but if you are uncertain, again, try it out on a test brush, or test map. You can also use the Tool Bar chart for a reference since the commands are labeled the same in both the Menu Bar and the Tool Bar

Tool Bar

The Tool Bar will probably the menu you use the most. It contains all the icons you need to start editing with UnrealEd. Click the picture for a detailed reference to each button and it's function.

Browser

The Browser has four modes:
Textures
Classes
Music
SoundFX

At the bottom of the Browser is a set of buttons that change with each Mode. They should be self explanatory, but I will refer to them as necessary. There is a lot more on the Texture Browser later on in this tutorial.

This is the Browser in all four modes Mode:

The Four View Windows

This is the where you will spend most of your time. There are different modes for all views that will be explained in each section.

Basic Terminology

Object - Everything we add to our map is an object.
Brush - A brush is a geometric shape, the basic building blocks or your level.
Active Brush -Also called the Red Brush or Building Brush, this is the red brush you use to add brushes to your map.
Actor - An actor is any non-brush object you add to your map. PlayerStarts, monsters, lights, inventory items, weapons, triggers, etc... are all actors.
Icon - An image used to represent an item. If you use a graphical interface (Windows,etc...) then you should know what an icon is.

Window Size

You can resize the view ports by clicking the bars between them and moving their position. Be careful with this, because you can crash the editor if you resize the windows with any other windows open (like the 2D Shape Editor, or Floor Lofter).

Colors

All the brushes in UnrealEd are color-coded for your convenience, making it easy to tell from any view what type of brush it is. Selecting a brush will highlight it a brighter shade of the same basic color.

2D Views

Top View - Overhead
X-Z View - Front View
X-Y View - Side View

This is a 2D view without the menu bar. To open the Menu bar press the green arrow.

This is the 2D view with the menu bar open.

The 2D Menu
The menus you will use most are shown below. For a description of each menu click on its picture.

2D View Movement
To move your view around in the 2D windows hold the left mouse button and move the mouse around. This will move your view up, down, left and right. Holding both mouse buttons and moving the mouse will zoom your view in and out.

3D View

This is 3D view. The green arrow in the upper left corner (under the menu bar) will open and close the menu bar, just like in the 2D views. The joystick icon next to that will update the 3D view in real-time*. When this is selected, ambient sounds are played, dynamic lights are displayed, textures are animated, and invisible brushes will be shown.

* You can also set each window to real-time updating by clicking in any window and pressing the "P" key. This works in both the 2D and 3D views.

There are five Modes you can select from the menu bar in the 3D Window. These are: Dynamic Lighting, Textures (full bright), Polygon View, Zone/Portal and BSP Cut Views.
Each is shown below with a brief description of each.
The Dynamic Lighting Mode will show lighting as you will see it in the game.

The Texture Mode will show you all the textures unlit (full bright) and not show any lighting effects. A nice feature if you are aligning or changing textures in areas you have already lighted and rebuilt.

The polygon (or poly for short) view will show all your brushes as colored geometric shapes.

This view will show all your zones as different colors. This will help determine if you have an area zoned (or sealed) correctly. Great for finding problems with zones.

The BSP view will show you how the engine "cuts up" the polygons for rendering. If you run into a BSP error (or hole, or HOM)* you can usually find the source with this view. It also helps you determine the speed of a particular area. the fewer the cuts, the faster the rendering.

* BSP errors, also called BSP holes, and HOM effects (short for Hall of Mirrors). They are usually caused by BSP cuts, especially several small cuts into one polygon. This is a common problem with 3D level design. They are almost impossible to eliminate entirely (most of the Epic maps that came with the game have them), but you can usually fix most of them. Big ones must be fixed, but the smaller ones will only appear in certain areas when you view polys at certain angles.

Map Perspective view

This view is a 3D view but the brushes are all wire-frame rendered instead of solid, or textures.

3D View Movement
Holding the left button and moving the mouse left and right will pan your view in that direction. Holding the right button and moving will roll your view up, down, left and right. Holding both buttons will slide your view up, down, left and right.

The Grid

Always use grid snapping. This will help align your brushes and prevent BSP errors. The default is 16 units and is usually sufficient. You can resize the grid scaling by right-clicking in any 2D window and selecting Grid. You can select a smaller grid setting (such as 8 or even 4) for adding smaller, detail brushes.

Brushes are "snapped" to the grid using the pivot point, shown in the editor as a red cross on a particular point. The default location is the center point. You can change this point by left-clicking to highlighting the brush and then right clicking on the point you want to make the pivot point. >

Putting This to Work

Imagine that the "void" in the 3D view is a large piece of clay. You will make rooms/areas by carving out of this clay the basic shape, and then adding details inside the space created. You will start by subtracting a cube from the world and then adding stuff inside it to make your room. Your primary tool is the active, or red building brush. I will call this "the red brush" or the "building brush." I might also revert to saying things like "reset your brush" and unless otherwise noted this will ALWAYS means your red brush.
Below is the Red Brush in the 3D view.

Your First Room

Adding, Subtracting and Texturing Brushes

Okay, lets make something! Your first room couldn't be easier. First let's see that Red Brush I mentioned. Simply left-click the Cube Tool. You should now see a red cube appear in all 4 views. As explained above, you can turn on the real-time updating now so you can see your movements in all windows. Remember, you can press the joystick icon by the green arrow, or click in a window and press the "P" key to do this.
Note: Any brush visible in the 3D view (except the red brush) will not show movement or even deletion until you rebuild.

Okay, we set the real-time updating in all windows and we still have this red cube. Wow, you say. In the Browser, select the Texture mode. Now we need to load a texture set. At the bottom press the Load button. A windows interface now opens and lists various files with UTX extensions. these are your texture packages. Each one is a set of textures. For now just load Ancient.utx. you can browse through the others later.
A bunch of new textures should now appear in the browser window. These are the textures in the file we just loaded. If you notice under the browser mode there are two pull-down menus. Both currently say all. The first one is to chose a loaded texture set. If you press the down arrow on the right you will see a list of loaded textures. Most are default engine sets, except Ancient, which we just loaded. Make sure Ancient is selected now.

You can press the [image] buttons to move through the loaded sets.
These buttons [image] will set the size (and the number of textures in the Browser window).
The next pull down menu is for the subdivisions within the texture set. This is just a way of grouping the textures in a set by type.

You can press the down arrow on he right of the set and see all the divisions. They should be pretty self-explanatory. For now, select All. You can do this by pressing this button [image] or by the pull-down menu.
Pick a texture by left-clicking it in the browser window. It should now be highlighted with a green border (like the one to the left).
This is the currently selected texture and will be used until you change it.

Having selected a texture, now press the Subtract button and you should now have a cube with the texture you selected. To change any texture left-click it in the 3D window. This will highlight it. Now if you select another texture it will replace the current one. Try changing the floor texture. Okay, we have a basic cube but it's not a level yet. First, we need to add light to it. But before we do that let's Rebuild.

Rebuilding Geometry

Press F8 to open the Rebuild window. This window looks complicated but you don't need to concern yourself with most of it. Rebuilding sets everything up and renders the dynamic lighting for your level. You have to Rebuild to see the results of dynamic lighting on the textures and to see the results after deleting a brush. When you first add rooms they are rendered unlit (full bright) and lighting won't affect them until you rebuild. For more info on the Rebuilder click the image.

Press Rebuild Geometry now to rebuild the level. Just use the default settings and everything is automatic. It only takes a few seconds. Wow...everything went dark in the 3D view! That's because we have no lights.
First, to see where you are in the 3D window, press Mode to get a drop down menu. Now select Texture. This will show you the Texture mode, and not use lighting. Right-click the center of the ceiling in our room and select Add Light Here.
Note: The "Add Light" feature is always on the menu no matter what you have selected in the Browser.
You should now have a little torch icon in all windows. This is your light. Change the mode back to Dynamic Lighting in the 3D view.
Press F8 and rebuild. Close the Rebuild window. Ah...lighting.
But this light is a little too close to the ceiling. Let's move it down some.

Selecting and Moving Objects
To move an object in UnrealED left click it any view. This will highlight it as the selected actor. Whenever you select an actor you can right-click to get a pop-up menu. You can access the actors Properties from here (more on that later). To move it down in the 2D views (except, of course the top view) left-click it to highlight it and then hold Shift and the Left Mouse Button. Move the mouse up and down and the actor moves with it. You can use this method in any of the views to move objects. Try moving the light around a little in each view to see how it works. In the 3D view the object moves with your view. So you have to highlight the actor and then move your view and the object moves with it. Now highlight the light, and move it down to the center of the cube.

Tips: If you hold CTRL instead of Shift the object will more the same way, except your view will not move with it.
Always left-click and object to select it. If you have an object selected and then right click another object it will select them both (a potentially hazardous situation if you are moving, deleting or changing an objects properties.

You can see how many objects are selected by right-clicking a selected object to open the pop-up menu and checking the first line. It tells you what kind of actor is selected and how many. See the image below. If multiple actors of different types are selected it will look like the second image.

This is the light actor when it's not highlighted and when it is highlighted. Note the greenish tint when it is highlighted? That is your indicator that it is highlighted. Most every actor will have this greenish tint when it is highlighted.

Adding Actors
We now need to place a starting point for our player in the level(so we can actually play it, or just to check it out in the game). In the Browser menu, select Classes. Look down the menu for Navigation Point. Press the minus sign next to it to expand the classes. You should see several classes, but the one we want is PlayerStart. Click it to highlight it and check at the bottom of the browser. On top the buttons down there, is where the editor tells you what is selected. Make Sure it says "Engine.PlayerStart (scripted)."
Now, right-click in the center of the floor of our cube and select Add PlayerStart Here. You should now have a Joystick looking icon there. This is your player starting point. This is the main method of adding actors to UnrealEd.
Note: Often when selecting classes from the Browser, you will accidentally double-click the class. This will pop open the UnrealScript window, showing the script for that class. Simply click the X on the Script Window to close it.

Rebuild your world. Now we need to save this level. Call it something original like TEST1 or something. Either choose Save Level from the Menu Bar or press CTRL-L. Name and save the level. Now press CTRL-P or choose Play Level from the Menu Bar. The log window will start scrolling and when it's done Unreal will open in your level. Now you should be in a small, ugly little room with a mysterious light source. That's what we just built! Bet we can do better.

There's nothing to do here so quit the game and return to the editor. As you know from the little play-test we did, this was no fun. So let's add some more to this level. First off, it would be nice if the light were actually coming from somewhere. Let's make a Torch! There is a handy little torch flame included with the game. You should still have classes in the browser. Click the plus sign by navigation point to close it, and then expand Light. Find TorchFlame and highlight it, but don't add it yet. First we need something for the torch to sit on. Right-click the red brush to highlight it. From the properties menu select RESET and Reset All Positioning. This is in case we rotated it or anything. The brush should move back to center (if you moved it). Now we need to change its size to something smaller than the room! Right-click the Cube Icon on the Tool Bar and open Cube Properties:

Height width and breath are the three dimensions of the brush and should be self-explanatory. Don't worry about wall thickness as that only applies to hollow brushes and we will only be working with solids for now.
Let's make this brush 64 units tall and make it 128 wide x 128 breadth. Make sure it is touching the floor, and touching one wall like below.

Now click the Add brush to world button and you have a block for the torch to sit on. If your new brush overlaps the PlayerStart icon then move the PlayerStart the same way we moved the light before. Now right-click the top of the new brush near the center and select Add TorchFlame here. A big torch should now appear in your map. Using the 2D windows, center the torch on the block. Also, move the light we added previously closer to the PlayerStart. Rebuild again and you should have something like the image to the left.

Tip: You always start a room by subtracting the main room and then using Add we add details and other objects to the room.

When we added the PlayerStart and the TorchFlame you got your first taste of adding actors to the world. You will use this method to add most all actors to the world. Let's add some decorations to the room to get some practice adding actors. Make sure the Browser is on Classes. Now expand Decoration (press the minus sign). Expanding this will give you a long list of items. These are all in-game decorations. If you have been playing Unreal SP then you will recognize most of them. Keeping with our ancient theme of this map lets add a couple pottery jars to this room. Expand Vase and you should see pottery 0,1 and 2. Add one of each to the floor in front of the TorchFlame holder we built, like this:

Notice in the 2D views that the actual meshes of the decorations are drawn. This gives you a great idea of it's actual in-game position and it's size. Rebuild and test your level. You may have noticed you can shoot these pots and destroy them. In a minute I will show you how to place objects inside these vases so you can get goodies when you shoot them.

Moving and Rotating objects

Okay, when we start this level our player is staring at the wall. That's okay, but it would be better to see something when we start. Lets turn him around so he is looking at the torch when we start. To rotate an object in UnrealEd left-click it in the overhead view to highlight it. See that red arrow coming off the icon? That is the direction it will be facing in the game. Now press the Rotate Icon. Still in the overhead view hold CTRL and press the right mouse. When you move your mouse back and forth you will see the object rotate and the red arrow tells you which direction it is facing. If you hold CTRL and the Left mouse button it will rotate on the Z axis (up and down).
This is the PlayerStart icon's direction arrow. Rotate the PlayerStart around and look in the 2D views to see which way it facing. Now I will show you how to set it back. While it's still highlighted, right-click it and from the menu choose Reset and then Reset rotation. The arrow should be pointing back at the wall again. Okay, rotate the PlayerStart so he is facing the torch flame.

Tip: You can rotate actors without pressing the Rotate Icon. Simply hold CTRL and right-mouse in a 2D view will rotate the object.

Let's reposition the player start a little. Click back on the camera view icon (1st icon in the 1st column). Now click the PlayerStart in the overhead view and hold Shift and left mouse button. Move the PlayerStart back against the wall. Now do the same thing with the pottery decorations and move them. I placed mine beside the torch holder like below. Rebuild and play-test the level.

Adding More Rooms and Changing Textures

Okay, this level is still pretty boring. Lets add some more rooms and stuff to spice it up a bit. First thing to do is make sure nothing is highlighted (click the Select None Icon on the Tool Bar). Now from the Menu Bar under Brush select Reset and Reset All. This will reset our red brush back to the starting position and reset its rotation and scaling. Now lets build a bigger room next to this one. Right-click the Cube icon and let's size this one a bit bigger than the first. Make it 512 x 512 x 512. In the overhead view move the red brush so it is next to (and touching) the first room.

Now press the Subtract button. In the 3D view you should now have a big room next to the first one. Let's retexture this room. Click any wall in the room to highlight the texture.

Highlighted Texture

We are going to retexture the whole room so select all the textures in this room. The fast way to do this is to press Shift-B to select the whole brush. With one wall highlighted press CTRL-B and now the whole room should be highlighted. Now in the Browser change it to Textures. Choose the Ancient Textures, and then choose Wall. Click one of the wall textures in the Browser and now are new room should be re-textured. We don't really want the floor and ceiling the same as the walls, so lets change them. Click on the floor in the 3D view to highlight it. Now if you press CTRL and left-click any other surface it will add that surface to your selection. Remember: hold CTRL while selecting surfaces to select multiple surfaces. So with the floor selected hold CTRL and select the ceiling. Now only the floor and ceiling should be highlighted. In the Browser select the Floor textures the same way we selected Walls. Pick a texture you like and click it to change the floor and ceiling to this texture.

Duplicating Brushes

There are a couple ways to duplicate a brush. For this exercise we are going to learn to Copy a brush, or more exactly, to copy the brush to the red brush and then adding it somewhere else.
Now we need a light source in the new room and to stay with our theme we are going to make another torch holder like the first. Click on the first torch holder we made to highlight it. Now right click and on the pop-up menu choose Copy Polygons, and To Brush. This will move the red brush to the torch holder and make it an exact copy. Now where ever we place the red brush and press add it will make a duplicate of the torch holder. Before you move it, click the Select None Button or simply click in any window so that the brush we copied from is not highlighted. Now in the overhead view, move the red brush into the new room we made. In the X-Z or Y-Z 2D views move the brush down so it is touching the floor. You will notice now that new room floor is much lower than the first room. That is okay, we will make stairs in a few minutes!

Okay, with the red brush positioned where you want the new torch holder press the Add icon. Now move the red brush to the opposite wall and add another. You should end up like this:

Now we need more torches on those holders. You can go to the browser, select Classes, expand Light and then highlight TorchFlame again. Right-click where you want them and add them like we did the first time. Or, you can use this shortcut:
Click the first TorchFlame we added to highlight it. Now press CTRL-W. This will make a duplicate TorchFlame, just slightly off-set from the first. By holding Shift and Left mouse button we can move this TorchFlame to the new location. In the overhead view move the new torchflame over the holder in the new room. In one of the side views (x-z or y-z) move it down to the top of the holder. When the first one is positioned, duplicate it again and move it over the next torch holder. You should now have something like this:

Rebuild and play the level if you wish. I know, There is no way back to the first room! We need to make some stairs to get back to the first room so lets do that.

Stairs

As always, start by resetting the red brush. Now click on the stair Icon in the tool bar. The red brush should now look like a set of stairs. We need to move it to position so hold shift and in the overhead view move it into the new room so it is touching the wall between the two, and center it.

In the side views you will notice the stairs are up in the air. Move them down to the floor of the second room. Check all the views to make sure the stairs are just where you want them. Once they are, simply click add and you have stairs!

Actor Properties

Let's add some more decorations to make sure we know how. In the Browser select Classes and expand Decoration. The first one is a barrel so lets add some barrel is this room. Highlight barrel and right-click in the 3D view where you want the first one and select Add Barrel here. Add two or three of them. Position them against the wall. Now let's learn how to add goodies to them. Left-click on a barrel to highlight it, then right-click to get the pop-up menu and choose barrel Properties. A new dialog window has opened. Take a minute to familiarize yourself with the properties window. Click the image to learn more about the Properties Menu.

All actors will have the same basic Properties window. There are several things we can set here but a lot more that will have no affect on this actor. The sheer number of fields of in the Properties Menu seems daunting. The truth is you only be concerned with a few fields at a time and most you ever touch. Each actor has a specific field with properties significant to that actor and almost everything you will set for that actor will be in that field. Since this actor (the barrel) is a Decoration, we will only concern ourselves with things under Decoration. Expand Decoration. You will see several properties here that are customizable. You should be able to figure out what they do from their names. Since we want to put stuff in the barrel, it makes sense that we would use the field Contents. Okay, let's put something in there. But how? I'll show you.

Go to the Browser. It should still be under Classes, which is what we want. Now collapse decoration by clicking the plus sign next to it and then expand Inventory. We want to put something in there we can pick up. Under Inventory there are two sub-classes: Pickup and Weapon. Weapons should be obvious, and Pickup is everything else we can pick-up, including Ammo. Well, you probably don't want to be running around in Unreal with just the Dispersion Pistol, so lets put a bigger weapon in this barrel. Expand Weapon and you will see all the Unreal Weapons there. Since I like the ASMD and it's the first one, lets place one in the barrel. Highlight ASMD in the Browser. Now in the Barrel properties window go to Contents and click where it says None. You will see some buttons on the right of it now. If you click Use now it should add the ASMD where it used to say None.

Now if you click the ... button it will close the contents field. Close the Barrel Properties now.

A weapon isn't much good without ammo, so collapse the Weapon sub-class and expand Pickup. The first Subclass is Ammo, which is what we are after so expand that. You will see all the Ammo for all the unreal Weapons now. Highlight ASMD AMMO now.

 In the Overhead view right-click another barrel, open it's properties, and add the ASMD AMMO the same we way we added the ASMD to the other barrel.
Close the barrel properties. Rebuild, save and then run the level. Now when you shoot the first barrel you'll get the ASMD and the next one will give you some ammo for it. The third one is empty.

Let's add another room but first, a hallway to connect it to the last room. Reset the red brush. Right-click the cube icon and open it's properties. Set its size to 256 x 512 x 256. Position this cube so it's even with the floor of the last room and connected to the first room like this:

Subtract this room. Now, I will show you a short cut to retexture the walls. If you have been following along, then the entire hallway is textured like the floor/ceiling of the last room. If we hold ALT and Right-click the wall texture in the room with the barrels, this will load that texture. Now if we hold Alt and Left-click one of the walls in the hallway it will copy the wall texture we loaded to it. Change the wall texture of both long walls in the hallway. Now resize the red brush to 512 x 512 x 512 again. Move it like we did the hall remembering to raise it in the side view until the floors are even. Now subtract it.

The walls are the right texture but the floor/ceiling is off. So using the method we used to change the walls in the hall, change the floor and ceiling in the new room.

Tip: You can add actors to the level the same way we did decorations. Weapons and pickups don't have to be inside other objects (like barrels). Simply highlight them in the Browser, right click in the 3D window where you want to place them, and Add it there. You can reposition it after it's added if you need to, like we did before.

Adding Creatures

Okay, this new room is going to be the home of a bad guy we get to fight! Now, we want the element of surprise on our baddie, so lets make a little alcove he will be in so we can get close to him without him seeing us. Resize the red brush to 512 x 256 x 256 and move it against the wall like this:

Subtract it and re-texture it. Now lets add the bad guy. In the Browser, make sure you are in Classes. Expand Pawn and then Scripted Pawn. These are all the creatures in Unreal. We hate the Skaarj so lets add one of those to fight. Expand Skaarj and you will see the two subclasses of Skaarj. Expand SkaarjTrooper. Highlight SkaarjGunner and add one to the alcove. He should be facing the right way but if he is not rotate him so that he is.

Before we go on, we are going to need light in the new room so copy the brush to the Torch holder and add a couple more torches in the new room. I put mine on either side of the alcove like this:

Now rebuild and run the level. Remember to get the ASMD and the ammo from the barrels before disturbing the Skaarj. Now go kill him! :)

Well, chances are, the Skaarj killed you because he has an Eightball. See if you can figure out how to give the SkaarjTrooper an Automag instead! In case you didn't figure it out, do this: In the Browser, under Classes, under weapons, select the AutoMag. Now open The SkaarjGunner Properties and expand SkaarjTrooper. There is only one field here: WeaponType (I think we are on to something here :)). Click where it says Class'UnrealShare.Eightball' and then Clear. If you rebuilt now the Skaarj would have no weapon, but let's since we have AutoMag already selected give him that by clicking Use (just like we did when we added the pickups to the barrel). Now if you rebuild and run the map the skaarj will only have the AutoMag and when you kill him he will drop it and you can pick it up. Cool, huh?

You add all creatures (good and bad) the same way. Each has his own properties you can customize. The creature AI is pretty good in Unreal but that is beyond the parameters of this tutorial. For more info on creatures and getting them to do what you want read Steve Polge's article on the Unreal Tech Page.

Texture Properties and Alignment

Alright, let's play around with textures a little bit. First make another room out of a cube 512 x 512 x 512. Place it on the left side of the alcove where the Skaarj and line it up so the floor is even with the floor of the alcove. Go ahead and subtract it. Lets give this room a new texture. First, go to the Browser, select Texture mode and then load the Skaarj.utx texture set. Pick on e of the Wall textures and texture all the walls in the new room with it. Now from the Floors section select the last one "Xpth" and texture the floor and ceiling with it. Lets add another leg on this room. Press the Cube Icon and make the red brush 256 x 256 x 512 and then press build, then close the window. Align your red brush up with the floor of the last room and make it extend out like a hallway. Retexture the walls and floors just like the last room. Now left-click the hall we just added, and then right-click and Copy Polygons to brush like we did before. Make sure nothing is selected, and move the red brush so that it is at the end of the new hall at 90 degrees, making a "T" shape. Now you should notice that floor and ceiling textures of the last hall don't quite match with the new hall. It doesn't look too bad with this texture, but dammit, I want them to line up.

The easiest way to align textures so that they all match is to align them all as either walls (for vertical surfaces) or floor/ceilings (for horizontal ones). To do this simply right-click the texture and from the pop-up menu choose align, and you will see your choices. You can select multiple textures as always before doing this and align them all at once. You can also do this by right-clicking, and opening the Surface Properties menu (more on that below). Align the floors and ceilings now. Once you have aligned the textures go ahead and add some lights to the parts we added. It's a good idea to make the light come from a light source. Look that Light textures and find one you like. Make brushes sized to match the texture. You can get the texture size by clicking the single texture button; the first one of these: .

Take some time to experiment with the different Surface Properties. There are a lot of settings under Effects you should get familiar with. Try making one of the walls in the new hall a mirror (check mirror under effects). Try a texture that pans (check U-pan or V-pan). Make three lights (each one in the a different room) and texture it with one of the Light textures. Place a light about 32 units in front of each light. Leave one light texture "normal," set one to Unlit and one to Bright Corners. Note the differences of each.

There are lots of things I could walk you through in using the texture properties, but then I wouldn't have anything left to put in the forthcoming Texture Tutorial :)

For now, here are all of the Surface Property menus and a brief description of them.

Most common settings are things like transparency, mirror, unlit (full bright), bright corners, panning, special lit. You can also set the texture panning in the Tool Bar. If you are not sure what each setting does, try it in a test map. Make sure real-time updating is set in the 3D window to view the effect. You must Rebuild to see the changes in the game.

These will rotate and flip the texture as indicated by the buttons. The Small and Big Diagonal will also scale the texture.

This will pan (or move) the texture in the direction of the buttons. The numbers indicate how many units it will move the texture.

This will align the textures in the direction indicated by the button. For example, if you want all your floor or ceiling textures to align highlight them all and select Align As Floor/Ceiling.You can also align by highlighting the texture, right-clicking and using the pop-up menu.

This will scale or resize your textures. One is the standard size, 2 is the two times the standard, etc...

This will show/hide selected objects in the editor. I usually use the buttons on the Tool Bar.

Other Shapes

One more thing to know is that rooms don't have to be cubes. Try making rooms out of Cylinders or Cones. You resize them the same way we did with the cube, by right-clicking the icon and setting the size under it's properties window. Add a couple more rooms out of cylinders, or cubes or Cones, whatever you like. Take some time to experiment and play around with the different shapes.

In Conclusion

This concludes the Basic tutorial. You should be able to build rooms, texture them, add lights, add actors etc... just enough to get you started. Play around and get familiar with interface and then move on to the advanced tutorials.

Note: There are several things we just "glossed over" in this tutorial. This tutorial is just to get you familiar with UnrealEd and to learn the very basics of level design. Subsequent tutorials will go more in depth on specific functions and tools of the editor.

TIPS

Start small. Hold on to that grand idea and make little test maps to try and work out something you are trying for the first time. Once you have it working right, save your map to refer to if you get stuck.

Forget vertex manipulation in UnrealEd. It is buggy at best and I almost never use it.

Always remember to subtract an area and then add stuff to it.

ZONES! A lot of things that were just brushes in Quake are zones in UnrealEd (such as water/slime/and Lava).

Remember there are a lot of useless properties in the properties window of the actors. most things you can change in a certain actors properties are in that actors specific fields. Such as Light for Lights, Decoration for decorations, Inventory for pick-ups, etc... Look for the actor specific fields when trying to customize actors.

Good luck, have fun and as always, if you need further help, let me know.

-Wolf

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Basic Light and Texture Tutorial- A Beginners Guide to UnrealEd

Introduction

To start with I am assuming you have a basic knowledge of UnrealEd and have read my BASIC tutorial or something similar. This tutorial is designed to get you started with basic light and texture procedures and will not go into great detail on either. The tutorial will give you enough information so that you can start doing cool things with light and textures, but it is not THE ONE document on the subject. If you are a seasoned veteran in UnrealEd then this tutorial will not help you much. If you are just starting or there are things you aren't sure about with lights and textures then read on!

Getting Started

First of all, to get the most out of Unreal, make sure you have the latest patch available at Unreal.com.
Second, if you have any problems starting the editor then get the latest UnrealEd fix here.
Third, To get the best performance out of the editor, limit what you have running in the background. Remove everything unnecessary including the screensaver to get maximum performance from Unreal and the editor. UnrealEd is a memory hog.

Tip: If you have been running a lot of games or other CPU resource sensitive applications, then reboot your system. This will help clear virtual memory and RAM and will allow maximum performance from UnrealEd. The more memory you have free, the less chance of a "random crash."
Last, If you have been a Quake/Quake2 editor and are trying out UnrealEd then it might be helpful to read this article: Making the Change.

Lights and Textures

Good lighting and proper texture can make or break a level. I have played lots of levels that I thought, "With the right lighting and better texture alignment, this level would ROCK!" All too often amateur level designers neglect proper lighting and texturing and effectively "ruin" their levels.

Texture Packages

Like most things in Unreal, the textures are stored in package files with the extension .UTX. Theses packages will be loaded with the maps but are not stored in the maps. When you load a map in the the editor it will only load the textures from the pack that are used in that level. To get the whole texture pack loaded you must click the LOAD button on the bottom of the Browser window and select the appropriate .utx file. This is the Texture Browser and the the texture with the green box around it is the highlighted one. This is the texture that will be put on any brush you add.

In most of the texture packs that ship with Unreal the textures are grouped by the texture application. That is, Lights, Walls, Floors, Ceiling, etc... are grouped together.

In the image above Certain parts of the browser window are numbered. Here is the key:
1) Selects Browser Mode (Textures, Classes, Sounds, etc....)
2) Selects already loaded texture packs. the arrows [image] will scroll through the texture packs.
3) Selects the groups within the texture pack.

The buttons

1) Scrolls through the groups in the current texture pack.
2) Shows the textures one Texture wide in the texture window.
3) Shows the textures two Texture wide in the texture window.
4) Shows the textures four Texture wide in the texture window.
5) Selects All groups in the current loaded texture pack.

Getting Started

To begin with open the editor (duh!) and choose a texture pack. Choose something that has light textures in it. I am using DecayedS so you might want to load that pack to follow along. Now pick a base texture and build a room with the cube brush, make it 256 high by 512 by 512. To change a texture on a brush face simple click it in the 3D view to highlight it.

Once a texture is highlighted, choose another texture in the browser and click it. This will change the highlighted texture to the one you just picked. Try it a couple times. Good. OK, now we want to change all the walls to a different texture. To make multiple selections hold CTL while clicking it. Also, when making multiple selections, if you hold CTL and and click a texture that is already selected it will "un-select" that texture.

Hold CTL and select all the walls then go to the browser and pick a nice wall texture to change it to. Click this texture in the browser and all the walls should be re-texture now. After making multiple selections and re-texturing it's a good idea to click the "Select None" [image] button to make sure you don't accidentally retexture something again. I usually click this button out of habit before selecting a new texture, just to make sure.
Okay, now that you know how to change a texture, go ahead and change the floor and ceiling.

Tip: You can select all the visible textures on a given brush by highlighting one texture on the brush and then pressing SHIFT-B to select them all.

Now that the room is textured, we need more area to work with. Copy* the room and move the brush just to the "north" (one grid square) of the first room in the overhead view. Now subtract the brush and you should have two identical rooms.
We are going to make a window between the two rooms. We are going to make this window 128 high and 128 breadth. We know that the two rooms are 16unitss apart because we placed the new room 1 grid square from the first one (one grid square = 16 units). So we need to make this window 16 units wide to fit between the two rooms.
So right-click the cube icon and go to Cube Properties. Make the cube 128h x 16w x 128b and then position it between the two walls.

I know what you're saying....."Wolf, I can't get the damn brush to line up correctly between the two rooms!" I planned this to show you something :-)

To get the brush to line up correctly you can do two things: a) you can right-click and empty spot on the grid and then resize the grid to 8 units.
b) you can highlight the red brush by clicking it, and then right-click one of the "corner" vertices and the brush will "snap to the grid" using that vertex. UnrealEd snaps by the "pivot" vertex (center vertex by default). The "pivot"vertexx will have a little cross on it.

Once you have the brush lined up right Subtract it. You should now have a nice square hole between the two rooms.

In casee you didn't read the BASIC tutorial (you should have!) then here is how to copy the room: Move the red brush just off the room in the overhead view. Now click one side of the first room brush. This should highlight the brush. Right-click and go to Copy Polygons To Brush. This will make the red brush an exact copy of that brush. Now click anywhere in the window or click the Select None button. Move the red brush to it's new location and click subtract. You have just made a copy of the first room!

The Window

We aren't finished yet. Yes, we have a hole, but we want an actual Window there. First step is to find the Window Texture we want. There are some good ones in the Genln.utx pack so go to the browser and load that pack, then go to the Glass group. Click the Select None button (just to be sure) and then select a nice glass texture and highlight it.
Now right-click the sheet Icon. We know the size is 128 x 128 (the default) so we shouldn't have to change that, but we do need to change the sheets orientation. Right now it's set on the Floor/Ceiling, so we need to click the Y button. That should make it right. Now position the sheet just in the center of the "hole." Now to place it correctly you will have to resize the grid, so if you used the "pivot point" method to place the hole then resize the grid now to 8 units. Now place the sheet right in the center of the hole.

Once you have the sheet positioned then click the ADD SPECIAL Icon. Go to the Predefined drop-down menu and select Transparent Window and under Visibility click the 2-Sided button. Now click the Add Special button and you should have a window.

Tip: Most brushes in Unreal will be 1-sided. We made the window 2-sided because we want to be able to see it from both sides (from both rooms).

Collision

If we were to play this level now (aside from the fact that it would be totally dark since we don't have any lights) you would notice that you can shoot and actually move through the window. This is normal. The sheet brush we used to add the window will NEVER block objects. EVER. Sheets do not, and cannot block anything. Don't bother messing with the Collision Properties of the sheet, it won't help.
However, we do want this window to block objects, so lets figure out how to do that. UnrealEd is cool. The programmer knew that since sheets won't block anything there needs to be a way to stop objects from moving through sheets. We are going to use an INVISIBLE COLLISION HULL. Basically a collision hull is an invisible cube brush used to block actors but cannot be seen in the game.

Rules: 1) Invisible Collision hullscannott touch other geometry.
2) See rule one.

We need to place our collision hull just in front of the window, but not touching it. I usually make my collision hulls one unit thick. Since the collision hull cannot touch anything else, and we want it as close to the window as possible, let's size our cube brush so it is just smaller than the window and 1 unit thick (That would make it: 127 x 1 x 127). So size the cube brush like that and then place it just in front of the window and then click the AddSpeciall Button. From the Predefined menu choose (oddly enough) Invisible Collision Hull. Add one on both sides of the window. After you place them both, if you zoom in a bunch it should look like this in the overhead view:

Note: if you look in the 3D view you will see the invisible collision hulls. Click thereal-timee Updating button (looks like a joystick) in the 3D window and it will disappear.

Okay, we have done a little work so it is time to save it. First press F8 and click Rebuild Geometry. Use all the default settings. You should notice that everything went black in the 3D view. This isbecausee we have no lights! Choose the "Textures" mode to get a "full bright" view in the 3D window. We will add lights soon. After rebuilding then save the map (call it anything you like).

NOTE: There are other methods of adding windows and getting collision without using sheets and collision hulls. These methods use very narrow cubes with a transparent window texture on the visible faces. This tutorial will not cover these methods, as they are more advanced.

You will probably want to change the texture on the "hole" the window sits in. The easiest way to do this is to select one face of the hole, SHIFT-B to select the whole brush and then choose a new texture. Pick a trim texture or something that looks like a window frame. You will probably have to play with the alignment to get it to look right.

Texture Properties

There is a lot to learn about texture properties. There is a section in my basic tutorial about texture properties and alignment and if you haven't read that yet go do so! The following tips and tricks expand on that and will give you a good idea what the different properties do. This is the Surface Properties window for a texture. I will NOT outline each item, only the most commonly used types.

MASKED - This will "mask out" or hide the color 0,0,0 (usually black by default) in the color palette of a texture. This is used on grates, fences, screens, and stuff so you can se through part of it. There are several textures in DecayedS (under Mask) that you can add to a sheet somewhere in the level to look at it. They are Masked by default. Delete the sheet when you are through looking at it.

INVISIBLE - This will make any surface invisible in the game. Used on Zone Portals andCollisionn Hulls.

2-SIDED - Makes the face of the brush viewable from both sides. Should only be used on sheets (for now) because you will never see the "inside" of a solid brush.

PORTAL - Makes the surface a portal for zoning. Used on water/lava/slime, etc...and the invisible zone portals used to "zone off" your level.

MIRROR - Should be self-explanatory. Makes a reflective, mirror surface. Used with Translucent you will get a polished, reflective effect.

SPECIAL LIT - This surface will only be lit by lights that are set up as Special Lit lights.

UNLIT - This surface will not reflect light and will always be lit 100%, regardless of lights.

HI/LO SHADOW DETAIL - Increases or decreases the amount of shadow detail on the surface. Checking both of these boxes sets a special "Medium detail" flag.

BRIGHT CORNERS - This will "amplify" the light refracted by the surface. Useful for lighting those "hard to light" areas and forincreasingg the brightness of the surface for lights and other textures.

TRANSLUCENT - This makes the surface "see through." It partially "masks out" the darker colors in a texture. The "more black" the area, the more translucent it becomes. Used on water, glass, etc...to get a surface that you can partially see through.

MODULATED - This works like translucent only in reverse. The lighter the texture color the more it is masked out. Look at the texture pack Detail.UTX and add a sheet and set it to modulated and rebuild to see the effect.

Big/Small WAVY - This will cause the texture to kind of sway. You would use this on water or for other moving effects.

FAKE BACKDROP - This tells theenginee to not draw this texture, but to "display" the skybox here.

U/V-PAN - Will cause the surface to "move" in the U or V direction. Used for simulating movement on water,conveyorr belts, etc....

Texture Alignment

I could write a book on texture alignment, but basically all you have to know is that "if it looks right it is right." If you add a texture that doesn't line up on your brush, people notice. Reviewers take points off, and professionals laugh. Line up your textures!
The easy way to align them is by highlighting the texture and right-clicking and selecting one of thealignmentt settings. Floor/Ceiling is for horizontal surfaces and will help you tile your floors/ceilings correctly. Aligning as Wall aligns vertical surfaces, and Wall Panning will help you align multiple wall surfaces so the tile correctly as well. You can also access these alignment settings in the Surface Properties Window by clicking the Align tab. Again, the best way to learn this is to try it your self and see what you get.

Panning

Panning (accessed through the Surface Properties Window, Pan tab)will move the texture left, right up or down so you can align them "manually." You can pan textures by clicking the Pan Textures on the ToolBar [image]. Hold CTRL and left or right mouse to move the texture in different directions.

Rotation

You can rotate the textures in the Surface Properties Window, Rotate tab. The buttons there are pretty self explanatory as to which way they rotate the textures. You can also rotate textures "manually" with the Rotate Texture button [image] and holding CTRL and left or right mouse button.

Scaling Textures

Often times you will need to scale your textures, either up for large surfaces or down for smaller ones. Click the Scale tab in the Surface Properties Window. Simple scaling will scale the texture uniformly in all directions. Using the Custom Scaling settings you can scale itdifferentt amounts on the U or V axis. Use this wisely, as you can really distort a texture with it.
Tip: Scaling a texture too large can also make the texture look really pixelated and "fake".

Add Lighting

Okay, you should have a good idea what you are doing with Textures now, so we will have to add some lights to the level. If you added any sheets or anything to play with the texture properties remove them now. Rebuild and let's light this baby up!

The easiest way to add a light to the level is to right-click the 3D view where you want the light and from the menu choose Add Light. Easy enough? You can also hold the L key and right-click the map to place a light where the cursor is.

Okay, we had the two rooms and the window, so lets light it up some. First add a light in the center of the ceiling of one of the rooms. Rebuild and switch the 3D view toDynamicc Lighting. Now add a light in the second room. Rebuild and save.
With the lights right up against the ceiling it doesn't really light the room very well. Select both lights and lower them about 64 units. Rebuild again and look at the difference. If you want to see a "side-by-side" comparison, only lower the light in one room.

Now we have to add a light source. One thing that bugs me (and most map reviewers) is when you have a lightemanatingg from nowhere. That is, a nice light area with no light source to emit it.

For this tutorial I don't want anything fancy so I will add a plain square light on theceilingg. You can and should get more creative with your lights. First go to the Light group in the texture set and find one that is appropriate for a ceiling light. I used Light6 at the bottom. Now size the cube brush to 16 x 128 x 128 (or whatever is appropriate for the texture you selected) and place it on the ceiling like below.

Add the brush and texture the lower face with the light texture you chose. It should look like this:

TIP: to reduce the BSP cuts and thereby reducing your poly count add the light fixture as a SEMI-SOLID brush. It won't matter much for the simple level in this tutorial, but it will help a lot in larger, complex rooms with lots of polys.

We are going to have to lighten up this light some to make it look right. If you don't believe me then rebuild now and look at it. It doesn't really "glow" enough to be giving off the light does it. We are going to try two things. Okay,highlighth the light surface of the texture and right-click and open the Surface Properties window. Check the Bright Corners box. Close the window, rebuild and now look at the light. Now go change the surfacepropertyy to Unlit and rebuild and then look at it. The images below illustrate these effects:

Normal, BrightCorners, Unlit

Now, by using Copy Polygons to Brush you can make an exact duplicate of your light fixture by copying the size and textures to the red brush, move it to the other room, and adding another one. Do this now and then rebuild and you will have a light fixture in both rooms.

Before we move on to all the light properties, you should play around with the surface properties a bit. Change one of the floors to Mirror and Translucent, rebuild and look at it. Then Make the other one Special Lit and rebuild. See how it's dark. That'sbecausee a special lit texture will only reflect a light set to special lit, and we have none in our test level. Change the light in that room to Special Lit and then rebuild. Notice how everything else went dark? Okay, reset the light and the textures. Play some more, set a floor to Hi or Lo shadow detail and look at that. Try making setting it to Bright Corners. Play with all the settings we outlined above to see what they doexactlyy. You should get an idea of what each property does and an idea of when and where to apply them.

TIP: DO NOT make atextureetranslucentt unless there is something behind it to show through. If you made a floor for instance translucent and there was nothing below it but the void, well try it and see what you get!

Light Properties

If we open the light properties window you will see all the basic properties you do for every actor, but the ones most important to us for this tutorial are under LightColor and Lighting.
There are four settings that we will use most often and they are the three under LightColor:
LightBrightness
LightHue
LightSaturation

and under Lighting:
LightRadius

LightBrightness is just that, how bright the light is from 0 to 255.

LightHue is the color of the light. This can be fun to figure out at first. If you have trouble finding the right light color values try Ulight, a wonderful utilityavailablee at my web site (http://unreal.gamedesign.net). It uses Windows color picker and allows you to select the color and then gives you the Unreal color values forBrightnesss, Hue and Saturation. A basic color chart would show:

  • Red=0
  • Orange=25
  • Yellow=50
  • Green=60
  • Blue=150
  • Purple=190
  • White=255

LightSaturation is the amount of light that is reflected off a surface by color. The higher the saturation, the more colors it will reflect. 255reflectss all colors, effectively making the light white. Remember, the amount of saturation can affect the color of the light.

LightRadius is how far the light shines from it's source. Setting this correctly for the area is crucial to getting the right light effect in your level. Play with this setting a lot to get an idea of exactly how far the radius extends. it can also be helpful to select the Radii view in the 2D windows and select your light. A Redcirclee will appear around the light, indicating it's radius.

(note: LightRadius is set very low for this example image)

Dynamic Lighting Properties

Lights in Unreal have quite a few settings to get dynamic lighting effects. It isbeyonde the scope of this basic tutorial to outline each one. You set these with LightEffect

and LightType

The best advice here it to try them out and see what they do. Remember to turn "real-time" updating on in your 3D view to see the light effect in the editor.

Tip: Too many dynamic lights in one area can slow your levels down, as they can take quite a bit of your PC's resources to render. Use sparingly.

Coronas

(LensFlares)

Although there is a setting for it in the actors properties, Unreal has no true Lensflare effects. It does, however have coronas which can look awesome when used correctly. I will give you the basics on creating the coronas and then you can experiment to see what looks right on your particular light in your particular level.

To start with we need to pick a corona texture. There are some good ones in the GenFX.utx package. Load it now and go to the LensFlar sub group.

Pick one you like and click it to highlight it. Now pick a light in your level, and right-click it. Under the Lighting Properties set bCorona to true.

Expand Display and then click where it says Skin. Click the "use" button and the texture name should appear next to it.

Rebuild your level and should now have a corona on the light.

Note: if you do not see the corona in the editor go to Options on the menu at the top of the editor and then Preferences. This opens the Advanced Options properties window. Expand Rendering and then Software Rendering. Set Volumetric Lighting to true.

Tips and Tricks

Drawscale (under Display) will set how large the corona is. Most likely the default setting of 1.000 will be too large and look "funny." Scale it down. I use .2 or smaller most often.

The LightRadius will set how far away the corona is visible from. The farther you are from the corona the bigger it will appear. As you get closer it scales down. Setting the Light Radius affects when the corona comes into view.

When using colored lights with coronas I will often use two light actors. I set the light brightness to 1 for the light with the corona and then add another light with the actual settings I want the light to give off, and place it next to or under the corona light. That way I can set the LightRadius of the corona so it will look right, and still get the proper LightRadius for the light that will actual light the area.

This concludes the tutorial. If you have any questions, you can e-mail me through my site. You should have enough information now on the basics of lighting and texturing to get started. Make a test level or two and play with what you have learned. Use of dynamic light and textures can really enhance a level.

Good luck and happy mapping! -Wolf Wolf@gamedesign.net

©Copyright 2000 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial One - Custom Brushes

Wolf's Tutorial #1 - Custom Brushes

Introduction:

This tutorial will discuss various ways to make (and save for later use) custom shaped brushes. This is an intermediate tutorial and I assume you have used UnrealEd before, and can do basic things with it (such as make rooms, add lights, change textures, etc...). I will be using keyboard shortcuts so it is a good idea to print the Toolbar Reference in Creative Carnage's Basic Tutorial and the Keyboard Shortcut Familiarize yourself with the keyboard short cuts (invaluable) and the Toolbar (so when I say things like "click the Scale Brush icon" you know where it is). I highly suggest that you read a BASIC tutorial or two if you are just starting out, and then come back to this tutorial when you have a basic idea how to make rooms and such. This tutorial will walk you thru the process, step by step. It is suggested you follow along using the examples presented. The same prinicples will apply to any shapes you want to make. Once you get the hang of it then go experiment with your own shapes.

I will try to be thorough and use a lot of pictures in my tutorials. If at anytime you are not clear on something e-mail me and I will try to clarify it for you.

Other Game (Quake 2) level editors read this: There are many differences between Q2 style editing and Unreal Editing. The biggest is that in Q2 you used 6 brushes to make up a cube. EVERYTHING was added to the world, and you used CSG subtracting to carve "holes" in previously added brushes. In UnrealEd it is different. There are two types of brushes you add. Think of them ass Positive (added) and Negative (subtracted) brushes. The Subtracted ones appear brown in the the 2d views, and the added ones blue. You can only add brushes inside of subtracted areas. You use subtraction to make your basic room (remember the world starts out solid and you use the red brush like a cookie-cutter to carve out rooms) then add brushes into it for details. Any Q2 converts who used QED should be familiar with negative brushes. If you are used to Worldcraft or Qoole then you are used to using a brush to "carve" or "clip" out parts of a brush, and then deleting your carving brush. With UnrealEd you don't delete the carving brush. You shape the red brush around the area you want to carve out and then SUBTRACT it from the existing brush. If you are not clear on that e-mail me and I can give you a better example. Let's move on.

Part One - Intersecting

You should know by now that UnrealEd has built-in "primitive" brushes. Cubes, Cylinders, cones, spheres, even stairs. This tutorial will cover how to make other shapes, like wedges, ramps, etc. All these shapes will be built using only UnrealEd. If you have a 3D modelling program like 3DStudioMax and you intend to use that for making your shapes you will have to wait for a later tutorial. This tutorial will show you how to use the above mentioned primitives to make various shapes. But before I get into shape specifics let me teach you about some tools you should know about to help you better understand what we are going to do, and familiarize you with the terms we will be using.

Brushes

You should know by now that a brush is a shape (block or whatever) in 3-d level making. In UnrealEd there are different types of brushes. The red brush is your builder brush. It is the brush you will shape to make your brushes. Remember the UnrealEd map starts out as a huge block you carve your world out of. You will also use the red brush to add things back into the areas you carve out. Wether adding or subtracting brushes, the red brush works the same way. You shape it, position it, and then click the appropriate button (wether you are adding or subtracting) to make your brush. At this point you should open UnrealEd, load a texture set (I am going to use NaliCast.utx but you can use any you like), and build a 512 X 512 X 512 room. Add a light in the middle so when we rebuild it doesn't go dark. Let's build our first custom shape.

The Wedge There are several ways to make a simple wedge. For the purpose of this tutorial I will show you two ways. The second way is much simpler, but go thru both methods so you can learn how to use Intersect and Deintersect (two things you HAVE to know to make custom brushes in UnrealEd).
The Wedge Torch Holder
Okay, your room is built. Now you want to put a wedge along one wall to act as a torch holder. The overall dimensions of the wedge will be 128(h) x 64 x 64. So lets resize our red brush by right-clicking the cube icon and adding those numbers in. Click "build" and you should now have a proper sized red brush. Move this red brush to the center of the room (so it's not touching anything else) and click the ADD BRUSH button. You should now have a 128 x 64 x 64 rectangle in the center of your room, hanging in the air. Now, without moving the red brush, click on the SKEW BRUSH button. Now we skew the brush until it looks like the figure below.
Now that we have the area we want to move surrounded by the red brush, we need to do one more thing before we subtract it. Click the INTERSECT tool. What this does, is reshapes the red brush. It will "cut away" any part of the red brush that is NOT touching a solid. Press it and see what happens.

now, the red brush is only where we want to subtrac, so go ahead and click the SUBTRACT BRUSH button. Now where the red brush is the rectangle we started with is brown. You can see that in the 3d view only the blue brushes show up. That is bcause the brown brush is a "negative" or subtrated brush, and is basically "not there" to the naked eye. "But Wolf, I don't want the wedge to look like a two-toned rectangle in the 2d views! How can I fix this? Easy. The game engine prefers you don't have a bunch of negative brushes hanging around anyway, so let's fix that. First you have to reset your red brush so it's not skewed anymore. Right click the red brush and select "reset" then "reset scaling." Now go to cube properties and resize the red brush to 140 x 80 x 80 and click "build." What this does is makes the red brush bigger than the original rectangle. Now position the red brush so that it totally surrounds the original rectangle, and doesn't touch anything else.

Now click the INTERSECT button and your red brush should re-shape itself to to the wedge. The intersect tool will "ignore" subtractions and only shape itself around additive brushes. This will make one wedge shaped brush instead of a half-positive, half-negative brush.

Now move the new (wedge shaped) red brush to where you want the wedge to be against the wall.

Click the ADD button and you should now have a wedge against the wall. Now highlight the original blue rectangle and the brown subtract we took from it.

Make sure nothing else is highlighted and press the delete key. Answer "OK" to the pop-up window which is asking you to confirm the deletion. Now rebuild the map and you should have a wedge against the wall. You can add a torchflame actor on top of it (like I did in the first picture) or whatever you want.

Saving your Brush

Great, we made a wedge! If you are using this as a torch holder you are probably going to want to use it again, so let's learn how to save it so we can reuse it later. The Red brush should still be "wrapped around" the wedge. If it's not, simply highlight the wedge, right click it and choose COPY POLYGONS and TO BRUSH. Now it should be back. Other tuorials on the web have told you to save the brush (as a .u3d file) and then load it when you want to use it again. That would be great if the tool worked! It's a bug in UnrealEd and doesn't work properly. What you really want to do is EXPORT the brush. From the file menu at the top choose Brush, and Export Brush. The default file type is .t3d and this is fine. UnrealEd likes these much better than .u3d files! Just name it (keep it simple, call it wedge1.t3d or something)and click SAVE. Now you have exported the brush to a good .t3d file. Let's test it! Click the CUBE icon to make your wedge-shaped red brush a rectangle again. Now go to Brush, IMPORT Brush, and find your Wedge (wedge1,t3d) and double click it. A pop up window will open. Make sure that SOLID MESH and KEEP ORIGINAL POLYGONS INTACT are selected and click "OK." You should have a wedge shaped red brush again. You can move this where you want it and ADD it to your world! This method works for all shapes in UnrealEd, no matter how many additive and subtraction brushes you have. Just remember to make the red brush totally surrounding the shape you want before INTERSECTING it. Always EXPORT and IMPORT brushes, rather than save/load.

Part Two - Deintersecting

So, you liked the wedge? But isn't it a little boring? Wouldn't it be cool if it were more, "jazzy?" How about a kind of conical shaped Wedge? Let's do it!

The Conical Wedge

This method is acutally easier than the Wedge example. I used the wedge to t3each you how to use the INTERSECT tool. Now we will use the DEINTERSECT tool! Okay. Let's start a new map. Again, make it 512 x 512 x 512. Now, since we want the wedge to be conical choose the cone tool. Right click it and select properties. Let's make this wedge 128 tall and an outer radius of 64, with eight sides. Don't worry about "cap height" or "inner radius" at this point. You should now have a red builder brush in a cone shape. First we need to rotate it so that the point is pointing down. One that is done, position the red brush against the wall where you want it, but leave half the cone "outside" your room.

Once you have the cone positioned right click the DEINTERSECT tool. The half of your cone outside of your room should disappear. Deintersect will cut away any part of the red brush that is not visible, wether it is inside another brush, or outside of your room.

Now click the ADD button and you should have a wedge shaped cone against the wall. Again, you can put a torchflame on it or whatever you want.

Wrap Up

Deintersect is a powerful tool. Along with Intersect you can use it to make some really cool, complex shapes. Remember: INTERSECT will remove any part of the brush that is not touching another brush, and DEINTERSECT will remove any non-visible part of the brush. The next tutorial will get into some more complex shapes.

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Two - Custom Brushes

Wolf's Tutorial #2 - Custom Brushes

Introduction:

This tutorial is for advanced intermediate tutorials. It is a follow up to my First tutorial, and you should have read that first.

Part One - Beyond the Wedge

Okay, you read my first tutorial and now you are wanting to make some more complex shapes. The wedges were nice but what about other cool items, like torch holders, computers, and things like that? This tutorial will show you how to do those, too, using the same techniques we used in the first tutorial. Also we will get into a couple advanced features of UnrealEd you may not know about.

Complex Shapes

For the purposes of this tutorial we will consider a complex shape an item that is made up of more than one shape. This tutorial will show you how to make these shapes and "convert" them to a single brush you can save and reuse. Since a good light source is essential to any level, let's make a torch holder first.

Simple Torch Holder

Let's start with out basic 512 x 512 x 512 room. We are going to build our torch holder in the middle or the room. That is so when we intersect the brushes later we won't have it touching anything. Let's start with a simple shape. Right-click the cone icon and set it's properties to a height of 32 and outer radius of 64. Don't worry about any other settings. Click the build button and close the properties dialog window. You should now have a squatty little cone in the center of your room. We'll need to rotate that 180 so the point is pointing down. Go ahead and pick a texture and then ADD the cone-shape to the world. You just made the base of the torch holder (where the flame will sit). Let's make the arm that holds the base to the wall. Right-click the cube icon and bring up it's properties window. Make the cube 16(h) x 16 x 128. Now line the rectangular red brush you just made up with the base like below.

Add the rectangular brush to the world. Now, that would work fine for a torch holer, but let's be a bit more creative and detailed with it. let's add another cone. Make this one 32 high and an outer radius of 16. Now, the cone should appear pointing down, unless you reset the brush earlier. We want the cone pointing down so this is fine. Line it up as shown.

Add it to your world. Now lets make a plate that holds the arm to the wall. Make cube 32 x 32 x 16. Line it up as shown below.

Now add it. If you have been following along you should have something like this:

Now make the red brush big enough to totally surround the brushes that make up the torch holder. Check all views and make sure the torch holder is completely inside the red brush. Now hit the intersect button and you should have something like this:

Now you can save the brush if you like. Remember: The save/load option doesn't work right, so EXPORT the brush (as a .t3d) and then IMPORT it when you want to use it again. Call it something creative like torchholder" or something. Here's a tip for you. Since we have to delete the old brushes anyway, while you've got the red brush around the parts of the torch holder, and BEFORE you intersect, hit the "Select actors inside brush" button. This should hilight all the brushes that make up our torch holder. Now Intersect. Make sure the newly shaped red brush looks correct, and then hit DEL to delete. Answer yes to confirm. Now move the red brush to a postion against the wall and add it. Rebuild the geometry. If your 3d view goes all dark, then open then under Mode on the window menu bar select TEXTURES instead of "Dynamic Lighting." There, it should be back now. Okay, let's put the flame on it. In the Browser, select classes, and then expand Light. Find the TorchFlame and click it. Now in the 3D view right click the top of the torch holder (the base, the first part we built). Make sure to not right click on the red brush. If the first option when you right click is "Brush Properties" then try again. If the first option is "Surface Properties" Then you are okay. Look down the menu at the second option. It should say "Add Torchflame here." That's exactly what we want to do, so select that. Now a torchflame should appear on your torch holder. Align it in the 2D views so that it is in the center of the holder base. Now rebuild geometry. If you switched the 3D view to "Textures" before, switch it back to "Dynamic lighting." It's still kind of dark in there so let's add another torch holder on the opposite wall. IMPORT the torch holder you saved earlier. We could have just moved the existing red brush where we wanted it, but we wanted to make sure we could import the brush back in.Once you import the brush, move it to the opposite wall and rotate it so it's a mirror image of the other torch holder. Now in the top-view 2D view highlight the torch flame. Press CTRL-W to duplicate it and then center the new torch flame over the new torch holder. Rebuild geometry again. Now add a player start, rebuild and then go test the room. I'll wait here for you. :)
The torch holders didn't show up very well, did they? Kind of high up and hard to see. Let's fix that. Start by going to the menu bar at the top and under Brush go to Reset. You shouldn't even have to click, it should automatically reset the brush and center it. Now, the red brush should still be "torch-holder-shaped" only upside down. That's okay, the next step will fix that. Right click on the brown brush that is our starting room. On the pop-up menu go to Copy Polygons...and To Brush. Yes, Copy Polygons to brush will make the red brush a "clone" of the one that is higlighted, in this case our start room. Now, in the top 2D view, move the red brush so that one side touches the existing start room and then press the Subtract Brush button (Remember before removing the red brush you need to either hit the Deselect All button or click somehwere in the 2D view so that the original room brush is no longer selected).

Now add a light (right click and choose "add light here") to the 3D view. In the 2D views position the light so that it is between the two torch holders, and just below them. Rebuild again and you should have something like this:

In the top 2D view move the PlayerStart icon to the far end of the new section of the room (farthest you can get it from the torches. Now rebuild and play the level again. You should now have a fairly wide, long hall with two big torch holders (with flames) at one end of the room. Walk around, looking up at the torches. The should look pretty good. But the room itself is boring. Let's try and spiff it up a bit, shall we?

Building Boxes

Whenever you are going to make complex shapes in an existing level you will want to build them in a separate part of the level and then move them where you want them. I call this little room a Building Box. I make them big so I don't have to worry about large objects touching the walls and such when I intersect them. Let's resize our brush to 1000 x 1000 x 1000 and move it out away from our existing hall.

Subtract it from the world and should have a big empty room now. I usually give this room a bright texture on all sides to make things easier to see in it. You can build all your items in this room, intersect them and move the brush where ever you want it in your level before adding it. Since I never add lights to this room, I always switch the 3D view to "Textures" when working in this room. Now, to add some details to our little hall. Let's go to the building box and build something to make our little halls less boring.

A Simple Column

I think a couple of narrow columns might be cool in there. So lets reset our red brush and then rezie it to 512 (h) x 64 x 64. You should now have a tall red rectangle in the center of your building box. Pick a nice column texture and then add the brush to the world. Now a tall slender column is pretty boring, even for this tutorial, so lets add some details to it. First select cone properties and size it to 32 height and outer radius of 64. Now line the red brush up with the bottom of the column as show.

Now rotate the red brush so the cone is "upside down" and then align the cone to the top just as you did at the bottom. Add the brush. Now make your red brush big enough to completely cover the new column and intersect it. Before doing so make sure all the textures you want are on the column and aligned properly. Once intersect you have a column brush. Save it if you like (EXPORT!) and then move the red brush to our starting room. Move the column brush to any corner and hit add. Move the brush again to another corner and repeat the add. Do this until there is a column in each corner, and one in along the wall (on both sides) in the center of the room where the two halves of the room meet.

Once you have added all the columns then rebuild geometry and check it out.

Go back to the building box and delete the old brushes that made up the column. Rebuild geometry and the old brushes should vanish. Now, we need some light at the end of the hall where the player start is. We could just add a light actor there, but we would need a light source to make it seem real, so let's build another light source. We should build something different this time. Let's make some sort of floor lamp. Now, we could go to decorations and choose lamp and add that, but that would be too easy. Besides it would do nothing to show you how to make custom shapes in UnrealEd. Let's build out own!

Floor Lamp

I can hear you saying, "Why don't we build a lamp that sits on a pedastal and has these neat metal cage things all around it?" Good idea you had there. Let's build it. We'll start off by moving to our building box. Let's build the hard part first, metal cage thingy. Now we don't want this too big and we are going to have to build it out of primitives. Let's make it out of little rectangles. We'll start with a cube so resize the cube brush to 64 x 64 x 64, select a texture and add it. Now we are gonna build some negative brushes and carve out our cage. resize the red brush so it sticks out of the sides, but doesn't touch the top or bottom. I made it 48 x 48 x 80. Line it up as follows:

Now subtract it. That should kind of hollow out your brush. Now rotate the red brush 180 degrees and subtract again.

Our cage is almost done, but we should put more little "bars" in it. So resze the red brush to 48 x 8 x 8. Now postion the new red brush like this.

What's that you say? "Wolf, I can't line it up like that! It won't go there!" Sure it will. It doesn't want to because our grid is set to 16 and when it snaps to 16 our little 8 unit wide brush won't line up right. To fix it just reset the grid. In a 2D view simply right click anywhere on the grid. Go down to "Grid" and choose 4. Now it should go where we want it. Once you get it where you want it, add it to the world. Now move the brush to the opposite side (keeping it centered like we did the first one) and add it. Continue until you have four little "bars" in your cage. Now we have the cage built, but remember this is going to be a floor lamp, so we need a base. Let's make the red brush 64 x 32 x 32 and center it on the bottom of the cage. Now add it. Now, the base of the stand: resize the red brush to 16 x 64 x 64 and poistion it at the bottom of the stand and add it. You should have something like this:

Now we simply have to build the red brush big enough to surround the new lamp, and intersect it. Move the newly lamp-shaped red brush where you want it and add it. I put mine in the center of the hall, touching the floor of course, between the PlayerStart and the second set of columns.

Now add a light actor right in the center of the cage part of the lamp. Now rebuild the geometry and make sure you set the 3D view back to "Dynamic Lighting." You should now have a lamp that casts some really cool shadows! "But Wolf, it doesn't look like a lamp. It just looks like a cool little cage giving off light!" Well, that's because there is nothing inside it. What we shoud do now is add a TorchFlame to the center. WOW! Much too big a flame for that little cage. Here's how we fix that. Click the TorchFlame to highlight it. Now right click it. A TorchFlame Properties window should pop up. Expand "Display" and go to DrawScale. It should be set to one now. We should make that about .2 (point 2). Now it should look better. Rebuild again. That is much better.You should have something like this:

Give Me More!
So this is great! Why don't we put an Arched doorway at the other end of the room, and maybe some steps and stuff... Go right ahead. IT's your map. But if you want to learn the best way to do Arched Doorways and stuff, you will have to wait for the next tutorial "Using the 2D Shape Editor."

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial 2D Shape Editor

Wolf's Tutorials Part Three - The 2D Shape Editor

Introduction

This tutorial is the third in a series by Wolf to help people with the more advanced features of UnrealEd. If you are new to UnrealEd then please go back and read a basic tutorial, and then Wolf's 1st and 2nd tutorials to get up to speed. If you are a seasoned UnrealEd user and are just not familiar with the 2D editor this will be easy for you!

Part One - 2D Shape Editor

You find the 2D shape editor on the menu bar at the top under Window. The fourth item down. Before you jump right in and get lost let me give you some info. First off, this tutorial is by no means the end-all of 2D Editor usage. In fact, it will be pretty basic. For a little more complete tutorial on the subject you should read the article on UNREALIZED by Myscha the Sled Dog. This tutorial will refer to that article from time to time, but even if you haven't read it you should be able to follow along.

Now, the 2D editor is labeled as "Experimental" and the reason is obvious: Bugs. It can crash if you don't treat it right. It is not perfect. And though it is absolutely powerful, it has it's limits. First of all it is basically a vertex manipulator you use to make a basic 2D shape and then "extrude" or "loft" it to a 3D shape. If you have used any 3D modelling programs (such as 3DSMax or Lightwave) you should be familiar with this concept. If not you can understand it in terms of UnrealEd by being able to make a sheet brush (but with many more than four sides) and stretching it upwards to make a 3D shape out of it.

Second, do not cross vertices in the 2D shape editor. It will give you a very strange looking brush and will do weird things when you add/subtract it. What the editor actually does is shapes your red brush (the builder) and then you can use it to make these complex shapes. Like I said, I am not going to go into all the things this editor can do, but cover a few important ones you should know about.

When using the 2D shape editor BACK UP OFTEN. Do not minimize the editor while the 2D shape editor is open, though you can (and often have to) resize the 2D editor window. Do not attempt to Undo with it or delete any vertices you add. This will only cause the editor to crash. If you mess up simply close the window and start again. I know, that's a pain if you are in the middle of a fairly complex shape, but this is experimental and limited. So you are warned!

Getting Started

Now you know where to find the 2D Shape Editor, let's see how it works. Open the 2D Editor and you should have a black screen with a blue grid and a big green triangle in the center. That triangle is your starting shape. We can add vertices to it and move the existing vertices around to get the shapes we want. I hope you know that a vertex is a point, the angle where to lines meet. To move a vertex in 2D editor is easy. simply click and drag. A little circle will appear around it when you do. Click a vertex and move it around a bit. see how it resizes the triangle when you move it? Okay, see the cross in the center there? That is your center point of your shape. For the first part of this tutorial it is not all that critical but later you will see why it is important to have it there.

Let's start with something easy. The first thing you will want to do is add a vertex. If you click any vertex of the triangle you can see how it highlights a "leg" or "arm" of the triangle. Click the top vertex and it should highlight the right leg. now click it again and it highlights the left one. This is how you select which leg you are working with. If you have a vertex with multiple lines coming out of it (and you will) just keep clicking that vertex until the arm you want is highlighted. When we add a vertex it will be added to the middle of this highlighted line. Okay, let's learn how to add a vertex. Go ahead and highlight the right arm of the triangle. The easy way to add a vertex now is to press CTRL-I (for insert) but you can also do it from the menu at the top under Vertex-then Insert. Now you should have a new line in the center of the triangle from the lower left corner to the middle of the right arm. You have just inserted a new vertex on the right arm and split that line in half. Click where the new line meets the right arm and you should get your little circle. You can now move this vertex around like all the others. You can see how we can make various shapes with vertex insertion. But remember: Do Not Cross Vertices. Do not drag a line over and existing line. About the Grid. The grid is set to 16 units. This translates to 16 units on your map as well, so you are editing in scale. If you make something 64 (4 grid boxes) units wide in the 2D editor it will be 64 units wide in your map.

Okay, before we get carried away close the 2D editor and reopen it again. This will reset the triangle shape so we can get started. Add vertex (it doesn't matter which arm) and then turn the triangle into a square (128 x 128--remember it's a 16 grid). Now you should have something like this:

I hear you saying, "That's great...but how do I make it 3D?" Remember I told you about "extruding or lofting." Well, you're bright, I'm sure you have already seen "Loft" on the menu a the top of the screen. If you choose Loft and then Extrude we can make our box into a cube. You may have to move or resize the 2D editor window to see it, but a dialog box pops up. Don't change Group Name and make sure the Loft Plane Z is checked. On the right for depth let's put in 128. Now click build. Nothing happened in the 2D editor but it just made your brush in UnrealEd into a cube. Close the 2D editor and look at it. You should have a pretty basic 128 sq cube. Now it would be silly to make that with the 2D editor when you have a cube button, but we did a square just to show you how Extrude works. How does it work? It takes the 2d square we drew and "stretches" it up to the depth we indicated in the dialog box. Try another box, this time with a depth of 64.

Okay, Let's make a different shape. Reopen the 2D editor. Don't change anything on the triangle. Go straight to loft-extrude, and set the depth to 128. You should now have a prism shape, or a triangle on it's side that is 128 units tall. You can use this to make a lot of different shapes. Let's build something useful, like an arch. Start by closing the 2D editor and building a room on your map. Make it 256 x 512 x 512. Go ahead and subtract the room and then open the 2D editor. Let's build an arch that is 128 unit stall. Oh no, Math! After years of editing in Doom/Quake/Quake2/Unreal I think in units of 8 so I know that it is going to be 8 boxes on a 16 unit grid. If you don't think in "base 8" then you may want to use a calculator! At any rate, start with a 128 square box in the 2D editor like we made earlier. Now, select the lower left vertex and that should highlight the bottom leg of the box. Now add another vertex. The bottom line should be split in half now. Click and drag the new vertex up until it is touching the new line it created (see below).

Extrude the shape to a depth of 64 and build. Close the 2D window and you should have a rather pointing, small arch thing. You should rotate this so the points are pointing down.

You can add this to your room now. Not too functional as an actual arch (too small to walk under) but you could use it for decorations or something. Try some other shapes, with more sides and different depths to get a feel for how it works.

Other Options

Okay, remember how we had to rotate the little arch thing? That is because we lofted it on the Z axis. That is, we told the editor the object was "laying flat" and we wanted it lofted "up." go in and make the shape again and this time before you loft it, set the Loft Plane to X. This tells the editor the object is standing as it if the bottom of the 2D editor window was "down" and we pulled it shape along the X axis. Now when you build it should be "standing up." Try making a shape and lofting it on the different Loft Planes to get familiar with how the editor uses loft planes to place your object. You are probably curious now what the other Loft options do. We have only used Extrude so far. Logically you should be able to figure out what the others do, especially if you are familiar with 3D models. I will run through them quickly.

Build Sheet: Does just that. Makes the shape a flat sheet. notice there is no dialog box for this lofting option. Revolve: See below Extrude: You already know Extrude to a point: see below Bevel: Works like extrude except it puts a cap on the object. The option to set the depth works just like extrude, and then the cap height option tells the editor where you want the bevel to taper to. It works the same way that putting a cap on a cone works.

Extrude to a Point.

This works like the bevel option only it will taper the object to a point. Think of it as bevel with a 0 cap height. This is great for making pyramid shapes. Simply make a square and extrude it to a point setting the depth however high you want the pyramid.

Revolve

Revolve is cool for making donut shapes and curved hallways. When you are using revolve the editor basically builds the object AROUND the center point (that green cross I mentioned earlier). Open the 2D editor and reposition the triangle vertices until the triangle looks like the image below. Be sure to have the whole shape either left or right of the center point. If you don't and you try to revolve it the editor will warn you (a rare instance when it doesn't crash if you do something wrong!:)

Now go to loft-revolve. In the dialog make sure the loft plane is Z and set the number of sides per 360 to 16 and number of sides to 16. Hit build. You should have something like this:

See how the editor lofts the shape around the center point? The number of sides per 360 obviously tells how many sides it will have in a full circle, and the number of sides tells it how much of the circle to build. If you set the #/360 to 16 and the number of sides to 8 is will build...anyone?....right! Half a circle! An Arc to you and I. Go ahead and try it using different numbers for the number of sides and number/360. But don't do something stupid like 12 sides per/360 and 128 sides. You will only get a stupid result. Going back to our example if we were to subtract that from the world, we would have a room rather round shaped with sloping floors and ceilings and a column in the middle. That column width is determined by the distance you put the 2D shape from the center point. The slope on the floor and ceiling are determined by the angle you have the "top and bottom" of the shape. Imagine the 2D shapes you make are like looking at a cross section of your 3D shape. If we made the starting shape square you would get a flat floor and ceiling. Play with it and see how you can slant the floor, ceiling, etc...

Arches and Doorways

Okay, you have learned a lot about the 2D editor and should have a fairly good idea how it works. Now I will show you a very powerful feature of it. In the 2D editor you can import BMP files to use as a "guide" for making your shapes that align with textures. This is how I (and the Unreal Level builders) make doors and arches. Let's learn how. To start with let's get a fresh map. close the 2D editor and in UnrealEd go to File and New level. Answer yes when it asks you to confirm. Select a texture set...let's use NaliCast.utx Pick a nice wall texture. Now build a basic room 256 x 512 x 512. Now in the texture browser choose ARCH to see the arch textures. Let's pick an easy one. See the very last arch texture (WRC-Door).

You can export this image so we can import it into the 2D editor. To do that highlight the texture and then click EXPORT at the bottom. Name it something cool like WRC-DOOR. If you noticed you can only export as a PCX file. I already told you that you must import a BMP file. So we need to convert the PCX we just saved as a BMP. The easiest way (and most universal since we are all using windows, right?) is to open the exported PCX in Windows Paint and then save it as a BMP. UnrealED exports the file to the Texture directory. Open Paint and find the PCX and open it. Now choose file, and save as and it should default to a BMP and already have it named for you. The only thing you need change is the directory, so that it saves it in the System directory. Once saved as a BMP close Paint and go back to UnrealEd. OPen the 2D editor and on the menu go to Image and Load image. You should see your BMP file there. Double click it to open it. Now your arch picture should appear in the 2D editor.

Now we need to manipulate and add vertices until we have surrounded the BLACK area of the image. Do this by lining up the lines with the edge of the stones in the image and add vertices, moving from left to right around the image. Now, you will see that we can't line the vertices up perfectly because the vertex wants to snap to the grid. You can go to Grid on the menu and uncheck Snap to grid to fix this. But this makes it harder to line up the Straight sides.

One you have it like this then go ahead and extrude using Y plane and 32 depth. Now, the arch hole brush should appear in UnrealEd, only upside down. We can rotate this till it's right. Now, we are going to use this brush to "cut out" our doorway. What we need to do now is build a wall to subtract it from. First EXPORT your new brush. Call it NaliArch or something. Close the 2D editor and build a wall that "cuts" our room in half. Make this wall 256 x 32 x 512. Now select the arch texture we have been using and add the wall. Now, we can see that the arch texture is half as wide as our wall. Align your wall (on both sides) so that we have two arches like below.

Now go to Brush and import and import the arch brush. Now align the brush so that it covers the black portion of one of the arches on the wall. Use the 2D views to center the brush on the wall and then slide it until it covers the black portion like so:

Now if you left snap on in the 2d editor it won't line up perfect. That is fine for the purpose of this tutorial. Once you have the brush aligned correctly then subtract it from the wall. You should now have a hole where we want it (the black part should be gone and a hole exists where the black used to be). Now do the same thing to the other arch on the wall. You should now have a room separated by a wall with two arched doorways in it.

What's really cool is that if we wanted to put doors in those door ways, we could use the same brush we used to cut the holes and the doors would fit perfectly! You can add a player start and lights and rebuild and run the map if you want.

Arch Options

Now, if we wanted a true arch we could have simply build a 256 x 256 x 32 wall, floated it in the air (but not in out start room, it's not tall enough!) and put the arch texture on it and then cut the doorway out. We would have had something like this:

Here's a little project for you. Build a room like this screenshot and see how close you can match it using what you have learned today. If you like send me the .unr (preferably zipped!)and I can see how well you did. You don't win anything and this is not a contest. I just like to see what my "students" learn! Note: I only used one (1) brush!

Play around with what you have learned about the 2D shape editor. It's a powerful tool, especially with the lofting options and the ability to import images to align your shapes with your textures. Again you should probably read Myscha's article at Unrealized.

Now, if you can wait until my next tutorial I will show you how to use the FLOOR LOFTER!

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Floor Lofter

Wolf's Tutorial Part Four - The Floor Lofter

The Floor Lofter

Introduction

This is the fourth in a series of tutorials from Wolf. If you are a beginner to UnrealEd it is suggested you go back and read a basic tutorial and then the first three in this series. This little tutorial will introduce you to the Floor Lofter.

Part One - The Floor Lofter

The Floor Lofter is an experimental program built into UnrealEd that will allow you to quickly and easily allow you to make a natural looking outdoor surface for your Unreal maps. It am sure it can be used for other structures as well, but natural looking terrain is it's strong suit. The Floor Lofter can be found in UnrealEd on the menu at the top under Window. Open the Floor Lofter. You should have this screen:

Now, if you were to just hit Render you would get a brush like this:

Basically, a cube cut up into 16 sections. If you check out the options across the top

it looks like there are several. Most, I believe, are not implemented as they seem to have no effect.

Here is the option and what it does:
Render Button - builds the (red) brush as per the rest of the settings.
Level - there are five levels of lofting, they go across the brush from left to right (in the overhead view).
Terra Grid - sets how many "blocks" there are across. The default is 4 x 4 and even if you change it, it has no effect. This is not implemented.
Cell Size - This sets the size of each block. Again, this is not implemented.
Render Scale - default is 1 which make the over all length and width of the brush 256 x 256. As you scale it up it increases the size of the brush accordingly.

Common Ground

Lets just show you what the floor lofter does. Start by loading the GenEarth.utx textures. Now, select a rocky one you like. Set your cube to 512 x 1024 x 1024. Subtract this brush, building a "room." Now choose a grassy looking texture for the ground. Think about how "hilly" you want your ground to be. Don't get too carried away yet, this is just a tutorial! Open the Floor Lofter. First of all, see that red line near the bottom of the window, that is going to be the bottom of your brush. The line at the top (with the vertices) will be the top of the brush. You can click and move that run line up and down to set the thickness of the floor brush. You can also click and move the vertices (and thus the lines of the top of the brush) and the top. By doing this you make hills and valleys, or high and low spots. The farther you raise a vertex on the top line, the higher the hill, and the lower you drag it toward the red line, the lower it will be. Do not drag a vertex from the top line below the bottom red line! You'll only crash and burn.

Now after you set the first screen the way you want it, then click on the "next" button [image] and go to the next level of lofting. There are five levels to set. Once you have set your terrain in all five levels, click the render button and then close the floor lofter. You should have something like this:

You can see this brush doesn't come close to filling our area. There are two things we can do. One is click on the "scale" button and stretch the brush till it fits. The second is to go back into the floor lofter and start again, but this time BEFORE we click the render button, we'll set the scale. Since our room is 1024 x 1024 we need to set the render scale to 4 (remember it starts off 256sq and we have a room 1024sq, and if we divide 1024 by 256 we get 4). This should fit our room very well. Now, the hills may be a little tall for this scale. If you think they are, just re-do them, keeping in mind the scale.
Note: you have to set the render scale each time you open the floor lofter. It doesn't keep that information.

When you get a floor brush that looks good to you then line it up with the bottom of your "room" and add it to your world. Now you might note that the scale of the ground texture is a little stretched. That's because UnrealEd "stretches" a texture to fit the surface of the brush. To fix this high light a face of the ground and press SHIFT-T (this will select all the matching textures in your level). Right click and go to Align Selected and align as floor/ceiling. This should reset the texture. If all went well, you should have a ground something like this:

As you fly through your level in the 3D window you can see how big (or how little) your hills are. Keep this in mind when making terrain. It's very simple: the higher you want the hills, the more you move the vertices.

Experimenting

You turn to play with the floor lofter! Remember, it's experimental so be careful and always experiment on a new level and not one you intend to keep. If, while you're playing around, you make an absolutely wonderful floor brush you just have to keep, export it like we did the brushes in the previous tutorials. Try new things, like only moving the vertices in one (of the five) levels to see what that does. Play with it and be creative and have fun!

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial MOvers

Wolf's Tutorials Part 5 -- Movers

Movers Part One (of Two)

Introduction

There are several types of movers in Unreal. I am not going to get into a WHOLE lot of detail on each kind, but I will cover the major types and give enough information on each one to get you started. As with anything in UnrealED the best way to learn is to do. I will give you a basic understanding of how different types of movers work, and then give you some specific properties of each and turn you loose. This is a two part tutorial. The Mover types covered in each part will be as follows:

Simple Movers Part One Rotating Movers Part Two Looping Movers Part Two Attach Movers Part Two

Simple Movers

Simple movers are made by adding a brush to the world you want to use for a mover, surrounding the brush with the red brush and intersecting it. Use the new red brush to add your mover. It is critical that the scaling and resizing of the brush be reset before intersecting. If you don't the mover will not work right. I usually use RESET ALL under the brush menu at the top, but you can also right click the red brush and select reset all. It is also crucial to have your textures aligned and applied correctly because one you intersect the brush, the mover will be identicle to it and cannot be changed.

Simple Door

So you got a level and you think you need a door in it huh? Well close the level you want the door in and start a new one. I will walk you thru making a simple door first, and then get into a couple other more complex doors. Once you learn the basics go back to your level and apply them.
Start off by loading a texture pack you like(I chose Starship). Next set your cube brush to 256 x 512 x 512 and subtract a room. Next reset your rush size to 256 x 512 x 32 and place it in the middle of the room, so you make a wall that divides your room in half. Add the brush. This is the wall we will place our door in.
Now we need to cut a hole in the dividing wall to place the door in. Reset the brush size to 256 x 256 x 32 and place it in the center of your wall and subtract. If you followed along you should have something like this:

Now, off to the side of the main room, build another room, this time make it 512 x 512 x 512. Pick a light texture for all six sides of the room. It doesn't matter what texture, this is just the room we are going to use to build our movers in and no one will ever see it but you. I will refer to this room as your builder room from now on. Ok, our hole for the door is 256 x 256 x 32. So we need to build our door 256 x 256 x 16. This way we will leave some room on either side of the door to make it look like a more realistic doorway. Set your brush size to 256 x 256 x 16 and make sure it is in the center of your builder room, not touching any of the walls and then add the brush to your world. Pick an appropriate door texture and apply it to the newly added brush.

Once you have the brush textured the way you want it, then reset the red brush and resize large enough to completely surround the door brush, but not touch anything else (I used 272 x 272 x 32). Position the red brush around the door brush and then intersect it and the red brush should wrap itself around the door brush.

Now take the newly formed red brush and move it to the hole in the center wall in the first room we made.

Now click the ADD MOVER button and you should get a purple brush in the door way. This purple brush will be the mover, and though it is invisible in the editor, it will look exactly like the brush we intersected. Now we need to set some of the doors properties. You'll need to either turn off the red brush (by pressing B in the window) or move it out of the way. Once the red brush is out of your way, then right click the purple mover brush to open up the Mover Properties window. There are only a few properties we need concern our selves with. First go to Object and InitialState. This tells the engine how we intend to open this door. The default is BumpOpenTimed. Like it sounds, when touched, this door will open for a specified amount of time and then close. Let's use that. Leave it at BumpOpenedTimed. Now open the Mover section. Whoa! Lots of properties there!

The first one to check is MoverEncroachType. This tells the editor what to do if someone is in the way of the door when it closes. The default is ME_ReturnWhenEncroached. With this setting, if you are standing in the doorway when the door closes, it will open back up until you are out of the way, then it will close. Let's go with that for now.
Next look at some of the other properties. We are going to use all the defaults for now, but often times you will want to set things like MoverTime (how fast it opens/closes), bTriggerOnceOnly (to open once and stay opened), bUseTriggered (If it will be trigger operated), and later on NumKeys (for how many key frames it will have).
Which brings us to Key Frames! Key frames are "points" where the mover will move to. Obviously you need these to tell the mover where to go. You can have 8 key frames (numbered 0 to 7). If you right click the mover brush you will see on the pop up menu under Mover Keyframes all the key frames numbered. We want to make our door slide straight up and down. One might think that when you move a mover to a certain position you would then select that key frame number.
This is OPPOSITE of how it works. We add the mover in it's 0 (or base) position. While it is there if we right click and select mover keyframes and keyframe 1 we can then move the mover to it's first key frame. When you move the mover to key where key frame one will be, then select keyframe 0 and it will automatically go back to its base position. Remember, When the mover is in the BASE or STARTING POSITION select KEY FRAME 1 and move it to KEY FRAME 1, then select KEY FRAME 0 and it will return to base. Think of it like "recording" where the mover will go. You pick one, move it to one and it remembers this path.
So while the mover is in it's base position where we added it, select key frame one, then move the mover straight up to where we want it to open to (I usually leave a little lip) and then select key frame 0 and the mover will snap back home.

At this point we have made a simple door that will slide up when touched, wait a couple seconds and then slide closed. Add some lights (on both sides of the door), add a PlayerStart, rebuild and run your map. Test the door a few times and make sure it works. If all went well the door worked just as expected.

Sounds

"But Wolf, my door is silent?" Why yes it is! That is because we didn't set any sounds to it. . . yet. Open up the mover properties and then click on MoverSounds. For normal doors and lifts we need to add a sound to each field, except MoveAmbientSound. The names of the different sounds should be self explanatory. In the browser change from Textures to SoundFX. Load an appropriate sound pack. I chose DoorsMod because I chose a futuristic theme. As you can see the sound file names tell you where to put them. Sounds with "end" in the name would go to ClosedSound and OpenSound, and ones with "start" would go in OpenedSound and ClosedSound. Easy enough, huh? once you have added your sounds the Mover Properties dialog should look like this:

Now you have fully functional door!

Double Doors

We could have just as easily made the door slide open to the left or right by setting different key frame positions. Let's make a double door that slides open to the side. Start by making a cube brush 256 x 256 x 32 and aligning it against a wall then subtracting it.

Now click on the starting room brush to highlight it. Let's make a copy of the first room. Right click and select Copy Polygons To Brush. Line it up like the image below and subtract it. You should now have another room, connected to the first by a small doorway.

Resize your cube to 256 x 128 x 16 and drag it into the builder room. Again, make sure it is floating in the air and not touching anything else. Add it to your world Select an appropriate door texture. Once you have it textured and aligned as necessary, intersect it with the red brush like we did the first door. Drag the red brush into the new door way we created between the two big rooms and align it to one side.

Now add a mover. Set the key frames so that the door will slide open to the side and out of the way like below:

We need two doors for this double door so go back to the builder room, intersect the new door brush again and bring the red brush back and line it up next to the first door. Set the key frames of this door to open the opposite direction of the first.

To get these doors to open simultaneously we are going to make this door open by a trigger. Select both movers (highlight one, then while holding control highlight the other) and open the Mover Properties dialog. Under Events set the tag to something unique (like slider1). This is the name of the door set the trigger will open.
Under Object set the InitialState to TriggerOpenTimed. While you have properties opened you can also set the sound like we did before. Now we need a trigger to open the doors. in the browser choose classes, go down to triggers and expand it. Near the bottom (next to last) you will see one that just says Trigger. Click on that (don't expand it). Now add the trigger to your room, and move it right in the middle of the two doors, near the bottom (see below).

Now open up the trigger properties and expand events. Under Event put the name of the two doors (slider1 in our example). Now, add some lights, rebuild and go check it out. When you approach the double doors they should slide open in opposite directions simultaneously.

Lifts

Our third simple mover will be a lift. A very simple one. You build a lift just as you do a door. First we need a place for the lift. Go to the newest room we made and duplicate it (copy polygons) and add it on top of the existing room. Now set your cube brush to 16 x 512 x 256 and position it like below. This will make a floor/ceiling, dividing the room in half height-wise.

Select a floor texture and add this brush to the world. Now drag the red brush into the builder room. As you can see this brush is too big (or rather, the room is too small) to intersect this brush without touching anything, so either expand your builder room or build another bigger one. Once you have the room big enough go back and duplicate the floor/ceiling we just made (Copy Polygons to Brush) and drag the brush into the now bigger builder room. Make sure it isn't touching anything and add it to the world. Select whatever textures you want, and intersect it with the red brush like we did the doors. Now drag the red brush to the room where we want the lift and lay it on the floor. Align it so it is opposite the floor/ceiling we added earlier.

Now add the mover and set keyframe one so that it is even with the floor/ceiling like below.

The only properties we need to change for the lift is under Object InitialState. Change it to StandOpenTimed. It works like bumpopentimed, but if we use that the lift will go up as soon as you touch the edge of it, leaving you behind. Standopentimed waits until your on the lift before it moves. Set the sounds for the lift, add a light in the upper part of the room and rebuild. Go try it out. Go on, I'll wait here.

Summary

You should have a pretty good idea of how to make basic movers now. You can set more keyframes if you want, but under Mover Properties mover set the NumKey to however many keyframes you have. Remember key frames start numbering at 0 so if you have just set key frame 3 then you have 4 key frames.

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Movers 2

Wolf's Tutorials Part 6 -- Movers

Movers Part Two (of Two)

There are several types of movers in Unreal. I am not going to get into a WHOLE lot of detail on each kind, but I will cover the major types and give enough information on each one to get you started. As with anything in UnrealED the best way to learn is to do. I will give you a basic of understanding on how different types of movers work, and then give you some specific properties of each and turn you loose. This is a two part tutorial. The Mover types covered in each part will be as follows:

Simple Movers Part One
Rotating Movers Part Two
Looping Movers Part Two
Attach Movers Part Two

Introduction

This is Part two of the Movers Tutorial. If you haven't read Part one yet, go back and do so first. I'll be here when you get back and I will make everyone else wait for you before we get started. :) In part one we covered basic movers, a couple simple doors and a lift. This tutorial will cover rotating movers and other types.

Rotating Movers

Rotating Door

This part would probably have been in the first tutorial since this first mover we are going to make is not a true rotating mover, but a simple mover that rotates on an axis. Let me explain.

The doors we made in part one were sliding doors. They slid up and down, or side to side. The door we are going to make now opens like a door in your house. It swings open on its side axis. They are pretty simple to make, but a lot of people don't know how. We will fix that. You will be making swinging doors in no time at all.

Okay, to start with we need a starting room and a builder room. I suggest using the NaliCast.utx textures for this tutorial. Make the start room 256 x 512 x 512. Make the builder room 512 x 512 x 512. Now make short hall way, 128 x 128 x 16 and attach it to one side of the room. Copy the first room and put it on the other side of the short hallway. You should have something like this:

Now the fun part! Go into your builder room and make a cube 128 x 128 x 16. Add it, then pick a good door texture (like SDOORN - it's 128 x 128). When you align the textures on your door, be sure to make the opposite side of the door go the opposite way. That is to say, you want the "hinge" part of the texture on the same edge on both sides of the door brush.

I mirrored the wall in those shots so you could see both sides of the door at once.

Once you have the door textures aligned, intersect the brush and drag it over to the little hallway between the two rooms. Light it up so it fills the hallway. Now, add a mover. Now when you highlight the mover, see that little red cross right in the middle? That is your PIVOT POINT. It is all IMPORTANT when making rotating movers! If you rotate the mover now, it will rotate on that cross as it's center point. Go ahead and rotate it and see then rotate it back. Now that might me cool for some effects, but not out little swinging door. We need to move that pivot point to a corner of the door. It is VERY easy to do. When the mover is highlighted, simply click the vertex you want to be the pivot point and the cross moves to that vertex.

Now when you rotate the mover, it rotates on the new center point, and you can see, that is how we want our door to open. With the door in it's original position, set key frame one. Now rotate the door to it's open position and click Key frame 0.

Now we have the door moving the way we want it we need to do something so it works right. But before we do , add a player start (where indicated below), add some lights, rebuild and test the map.

Notice that when we approach the door, it starts to open but then closes right back? That's because of the doors MoverEncroachType. The default setting is ME_ReturnWhenEnchroach. That simply means that if someone is blocking the door it will politely say "excuse me" and close again. We need to fix that if we want to be able to open the door from both sides. We could set it to ME_CrushWhenEncroach but that would cause our little player to explode upon impact with the door. That would suck because we wouldn't ever be able to see all the cool stuff on the other side of the door (not that there is anything in there yet, but our player doesn't know that!). Anyway, let's set it to ME_IgnoreWhenEncroach. This will cause the door to "pass thru" any blocking player. Not a very realistic effect, but sufficient for this step in the tutorial.

Rebuild and run the map again. Ahhh...much better. Well, almost. The door is "funny" the way it passes thru you when it opens. It's cool when you go back through it from the other side, but I can't live with that passing though me stuff! And it's silent! First go set the sounds like we did in the last tutorial. Now, reset the MoverEncroachType to ME_ReturnWhenEncroach. Now we need to add a trigger to activate the door. First set the mover's InitialState (under Object) to TriggerOpenTimed. Then under Events give it a unique tag (like "door1"). Add a trigger and set it's Event to the mover's tag. Now move the trigger in front of the door, set it far enough back so that the door won't hit you when you activate the trigger.

Now rebuild and try it again. Go on, I'll wait. HA! Gotcha. You couldn't get back through the door could you? That's because the door is only activated by a trigger and there is no trigger on the other side. There are two ways to do this. One, move the trigger to the center of the door and set it's collision radius big enough to activate it on both sides. The other is to duplicate the trigger and move the new one to the opposite side of the door. Let's take the first option.

Reset the collision radius on the trigger to 156. Why 156? Because the door is 128 units in length and we want to activate it before we touch the door (remember, if we touch it while it's opening it will just close again). now move the trigger to the center of the door.

Rebuild again and test. Presto! You have a swinging door that works! You probably noticed that if you kept moving forward once the door was activated, you still hit it and it closed. The work around is to reset the MoverEncroachType to ME_IgnoreWhenEncroach. The effect isn't nearly so Unrealistic (pun intended) when you actually see the door opening before you. Rebuild again and try it out. Now play with the door. Set it's encroach type to different things and see how it reacts, and play with the triggers collision radius. You should be able to find the best setting on your own.

Rotating Mover

The rotating door, though it rotates to it's key frames, is not a real rotating mover. It's time to actually build a real rotating mover now. You can start a fresh map, or continue working on the one you started with. It doesn't really matter. Either way, we are going to build the rotator in a new room. But first we have to decide what kind of rotating mover we want. For this tutorial we are going to make something fairly simple, just to get an idea of how to do rotators. Let's make a rotating wall trap. When we are finished it will look like this:

A Rotating brush is a brush that rotates on a specified axis. You don't need key frames for it to move. In UnrealEd (and every other 3d editor) there are three axes (the plural of axis) that we use to determine an object's location. They are X Y Z. You can differentiate these by looking at the 2D view window's properties and seeing that X is the top right window, and Y is the bottom right window. Z is actually the top view, because Z is up and down. A Rotating brush will move on these axis but the direction it rotates is determined using Pitch Roll and Yaw. If you have any flying experience (or simulator experience) you will understand these concepts immediately. If you don't then think of it this way:

  • Pitch= up/down (like a ball)
  • Roll=left/right (like a wheel)
  • Yaw=left/right on Z axis (like a revolving door)

[For a good explanation of Pitch, Roll, and Yaw go to this site: http://www.nasm.edu/GALLERIES/GAL109/NEWHTF/HTF541B.HTM]

Okay....first let's make a hall for it to go into. Coming off the second room in our little map, make a room 512 x 1024 x 512 so that it makes an "L" with the first two rooms. Like This:

Next thing we need to do is make another builder room. Our needs have outgrown the previous room, as the new mover we are going to make will be to big to properly fit in the old builder room. Go ahead and delete the old builder room and anything in it. Rebuild Geometry and then subtract a new builder room 2000 x 2000 x 2000. This should be plenty big enough. Once you have the room subtracted, then resize the cube to 512 x 16 x 512. Add the brush to the world, give it a wall texture and then intersect it like any other mover. This should make the mover a "wall" that will fill the hallway we made for it. We know we want this to be a rotating mover so we have to set that property before we add the mover. In the Browser, go to Brush, then Mover and click on RotatingMover.

Now move the red brush into the hall we made for it (somewhere in the middle) and then click on the Add Mover Button. If you highlight the mover and right click now, it should say "RotatingMoverProperties." now we have a rotating mover, but we need to tell it which way to rotate (see Pitch, Roll, Yaw above). We want this mover to rotate it's Roll, so that it is spinning parallel to the hallway.

So we need to set it to rotate on the Roll. To do that open up the RotatingMoverProperties dialog and near the bottom expand RotatingMover (see below). There are three fields here under the heading RotateRate. Oddly enough they are Pitch, Roll and Yaw. This is where you tell the engine how fast and which direction you want the rotator to move in. Guess which one we want to set? Right! Roll. And how fast? Well, let me tell you, 100 is pretty slow. So pick a speed about 1000 and see how you like that. Note: don't bother checking it now, we haven't activated the mover yet. Keep reading.

We also need to set some properties of the mover. First thing to do is give the trap an Event Name (something original and fun like....trap1). Next, under Object, set the InitialState to None. Under Mover, set the Numkeys to 0 (so the mover knows there are no key frames to worry about).

Add a light on either side of the mover, then rebuild and test the map. The mover should Roll end over end. Great. But what happens when you approach the trap while it's moving? Right, it goes back to the start and stops moving. That's because we have the Encroach set to ME_ReturnWhenEncroach. Open up the RotatingMover properties and under Mover set the MoverEncroachType to ME_CrushWhenencroach. Now rebuild and check it again. See what happens. Now if you run under the mover all is well, but if you touch it while it's moving you hear a scream and see body parts. But damn, that mover still goes awful slow! No too much of a challenge. Reset the RotateRate to 2000. That should do it.

Now we have a rotating wall in the middle of the hall you have to run under without touching it. Not a bad little trap.

Practice Makes Perfect

Now go build a rotating mover (any shape you want) and practice playing with the rotation direction (Pitch, Roll and Yaw) and with the rotation rates. You can make some really cool objects (not just traps) with rotating movers. Experiment and have fun!

Looping Movers

A looping mover is a mover that is constantly moving. Like our trap we just made. So technically you just made a looping mover. If you select LoopMover (like we selected RotatingMover) you will get a slightly different dialog box. It has nothing special (like the RotatingMover had is's own RotatingMover field section. No, it's all in the settings. You can set it to rotate (like we did the trap --only the RotationRate is under Movement). The only thing you need to set is under Object, InitialState. Set it to LoopMove. Let's make a quick one. Expand our hallway past the trap another 512 units (set the cube to 512 x 512 x 512 and place it against the hallway at the far (South) end. Subtract it. Back in the builder room make a cube 128 x 128 x 128. Give it a unique texture (I used Nalicast.utx Block2B) and intersect it. Now move the red brush to the new section of hallway and rotate it like this:

Move the brush near to, but not touching the South wall. If you're not already there, in the Browser select LoopMover and then add it to the world. It should look like this:

Now open the LoopMoverProperties and set the InitialState to LoopMove. Then under Movement set the rotation rate for Pitch to 2000.

Add a light and rebuild. Let's go check it out. You should have a cube slowly spinning against the wall. I know, not very practical, but hey, it loops and that is the point! I am not here to give you ideas for your levels, but to give you some tools to implement those wonderful ideas floating around in that twisted little head of yours! :) You may have noticed a weird effect on our mover. Only the side that was facing the light in the Movers "at rest" position was lit. There are a couple ways to combat this. One, select all the surfaces of your brush before you intersect it and set them to unlit. The other is to add lights all around the mover. One way of doing that is to give the surfaces of your brush (before you intersect it) the Special Lit property and then add lights all around it with the Special Lit (under Lighting) option checked.

Attach Movers

This is a cool type of mover. With this mover you can "attach" actors to it so that they move in relation to the mover. We will make three different Attach Movers in this tutorial.

Simple Attach Mover

The first is going to be a simple moving brush with an object attached to it. Start by moving your brush to the builder room and making the cube 16 x 128 x 128. Add it and give it a cool texture. Now intersect it and move it to the room where your PlayerStart is. Place the brush on the floor, in the northeast corner (upper right). In the Browser chooses classes, brush, movers, AttachMover and then press the ADD MOVER button. Set Key frame one to the Northwest corner (upper left) of the room. Now under Inventory-PickUp add an Armor just above the mover, but towards the north wall, away from the edge of the mover. Leave all the mover properties at their default except under AttachMover set the Attach Tag to "item1" (this is the tag of the attach mover and will also be the tag of the item we want to move with it--in this case the armor). Set the armor tag (under event) to "item1"

Now we have just linked the armor to the mover. The mover is set to BumpOpenTime so if we walk up to and bump the edge of the mover it should now slide along the floor taking the armor with it. Rebuild and test it out. Notice you can still pick up the armor. You can even ride the mover with the armor. This will work with other entities too. I have not played with too many different ones, but I have found that if you use a weapon it will move with the brush but you can't pick it up. If I discover how to do that in the future I will amend this tutorial. If you find out first, let me know!

Invisible Attach Mover

You can make a visible attach mover (or any mover for that matter) by simply selecting all the textures of a brush to invisible and then intersecting it and making the mover. Let me show you.

First go to the builder room and make a 32 x 32 x 32 cube and add it. It doesn't matter what texture you use because it will disappear shortly. Select any face of the brush and then right and open Surface Properties. Check the Invisible box. You brush should disappear in the 3D view. If you look in a 2D view it's still there. If you want to be able to see it in the 3D view then click the joystick ICON or click anywhere in the 3D view and then press P. Now resize the brush and intersect the invisible brush. Move the red brush to the start room and add an AttachMover on the south wall (opposite of the 1st attach mover) and place it near the ceiling. Set the key frames the same as the 1st attach mover. Now give the invisible mover a tag name under events (invis1) and add a trigger, giving the trigger event the same name as the tag on the invisible mover (invis1). Move the trigger to the middle of the room, near the floor. Set the mover's InitialState to TriggerOpenTimed. Set the AttatchTag in the mover to "item2" and add a Flare just underneath the mover, giving it a tag of "item2". Now rebuild and when you stand in the center of the room it flare should move across the floor and a couple seconds later move back. You can pick up the flare if you want. You can do some cool stuff with this if you use your imagination!

Moving Lights!

Which brings us to moving lights! There are a couple ways to do this. You may or may not have heard of the moving lighted brush. I can do that but with less than wonderful results. The best way I have found is using an AttachMover. Follow along and you can do it, too!

Start by making a hall running North off the first room. Make it 256 x 256 x 256. Now set the Brush to 256 x 256 x 512 and attach it to the new hallway like this:

Now, in the builder room we need to make another invisible brush. You can use the same 32sq cube we used before. Interesct it, take the red brush back to our new hall, and add an Attach Mover near the ceiling on the East end of the room.

Add key frame 1 at the far end of the room. Give the mover a tag of "movelite1" and an attach tag of "lite1" and make sure it's InitialState is TriggerOpenTimed. Add a trigger in the 256sq hallway, right in the middle (near the floor) and give it an event of "movelite1." Go to the Browser, choose Classes and expand Light. See the FlashLightBeam? Select it. Add a light just below the mover. Right click it's properties (making sure it says FlashLightBeam Properties) and give it a tag of "lite1" Rebuild and run the map. When you walk into the 256sq hallway the light should move down the hall, pause and come back. Cool, huh? :)

Wrap Up

We have learned a lot about movers in this tutorial. You have learned in a just a little while what it took me months to learn. As always, expand on what I have taught you and you can really do some amazing stuff with movers. The moving light thing is not widely known in the Unreal editing community and I thought about not adding it so I would have "a trick up my sleeve" when it came to releasing new maps. But that is not why I am writing these tutorials (obviously). It is to share what I have learned and make Unreal Editing a more pleasant experience for everyone brave enough to fire up the editor and attempt to CREATE something.

I wouldn't mind a mention in your readme file if you make a really kick ass level with the knowledge you got from these tutorials either. :)

Happy Mapping!

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial SkyBoxes

Wolf's Tutorials Part 7 -- Sky Boxes

Skybox

To make those cool skies in Unreal you have to learn to make a sky box. A sky box is merely a "room" where you make your sky/outdoor area and then a ZoneInfo that acts as a sort of camera and is displayed on ceilings/walls that you set to "fake backdrop." Think of the SkyZone area as the "Stage" and the walls you set to display it the monitor (kind of). So, there are basically 2 components need. A skybox, and a surface set to "display" it.

Basics

To start with load the GenEarth textures. Now make a room 512 x 512 x 512. Give the ground a nice ground texture and don't worry about the walls, yet. Add a PlayerStart to this room wherever you want. Secondly, make another room the same size off to the side, away from the room you just built. This room will be the SkyBox.

Inside this room make a box 256 x 256 x 128 "box" in the middle of the floor.

Now load the SkyBox textures. In your skybox select any wall and press SHITF-B to select the whole brush. Change the texture to the cloudy one at the bottom. Now select the whole "box" we added to the middle of the room and change it's texture to the ground (GRD) texture. Your skybox should look like this now:

If you look at the textures in the skybox package (the ones like LND1 LND2 etc...) you will notice they look like mountains but with a black background. These are masked walls that will be the sides of our sky box. First, make sure no textures are selected and then right click the sheet tool to bring up the sheet properties. Set the properties to X wall, U:256 V:128 like below:

Build the sheet then choose one of the LND textures (I used LND2_1) making sure no other surface is selected. Position the sheet like below:

Now, select the "Add Special" tool and open it's properties. Set the properties to Masked Wall and make sure Masked, 2-sided and Semi-Solid are selected. Click the Add Special button.

Now rotate the sheet 90 degrees and move it to the next position clockwise. Select the next texture (LND2_1b).

Add special again, and then rotate the sheet again and choose the next texture and add. Keep doing this until you have all four "walls" added around the "box" we set in the center.

Now we need to add SkyZoneInfo to the box. In the Browser go to classes, info, zoneinfo and click on SkyZoneInfo. The zoneinfo will act as the camera so the "displayed" sky will be from the perspective of this zoneinfo as though it were the "camera." Place it as shown below:

Now we need to add a sheet brush the same size as the sky box, so select sheet properties and set the size to U:512 V:512 and select Floor. Place this sheet just above the masked walls we added in the center of the box:

Choose the cloud texture and add a masked wall. If you right-click the cloud sheet and select u-pan or v-pan effect, the clouds will move.

Now we need to add lights. This sky zone is small so we can just add one light, near the ground and keep the default settings.

Now, move back to our first room. Now we need to set the second component to make the skybox work. Select every surface EXCEPT the floor and right click to bring up texture properties. Choose Unlit and FakeBackdrop.

Now add a light to this room, rebuild and test run the map. You just built your first skybox!

Tips

You might have noticed first off that there was a visible line between the cloud sheet and the walls of the sky box. you would fix this by making the skybox room and cloud sheet larger so that the cloud sheet stretches well past the SkyZone info and the edges are hidden behind the mountain walls in the sky box.

Also, the panning speed of the clouds is kind of fast. You can control this speed in the SkyBoxInfo under ZoneLight, by changing the TexUpanSpeed and TexVpanSpeed. The smaller the number the slower it pans. Try .5 or .3 for a good speed.

You can also set the ambient light of the SkyZone here. This won't affect the lighting in the "world" just how bright the sky and everything in the SkyZone appear.

You can also add other sheet brushes, with planets or moons on them. Just set them below the cloud sheet and make sure masked and transparent are set. Note: You can not have double transparencies (that is one transparent sheet over another in a sky box).

You can also set the light colors, but never give them dynamic properties or corona's (len's flares) as this will slow the map down considerably!

Homework

See if you can duplicate this screen shot.

Play around with different things in the sky box (lighting, texture panning, etc...) and see what you can come up with. You should have the knowledge to build some really cool skies now.

Notes:

In the Unreal Levels you will often notice two sky boxes. You can only have ONE sky box per level. They used two sky boxes in Unreal because they had one for hi-resolution and one for low-resolution.

Never use dynamic light effects in sky boxes. You can use non-solid sheets in the skybox instead of semi-solid sheets.

If I left anything out of this tutorial or if you aren't clear on something or just have questions about it, please e-mail me and I will answer your questions. Have fun!

-Wolf

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Water

Wolf's Tutorials Part 8 -- Water

Water

Water is one of the best looking effects with the Unreal Engine. To make water you simply make a zone, add a WaterZoneInfo, and give the "top" a water texture. That's basically it.

Simple Pool

Start by loading a texture set (whatever you like) and then loading Liquids.utx. Okay, pick a texture from the texture pack you selected (anything but a water texture) and then make a 512 x 512 x 512 cube and subtract it from the world.

Now, resize the red brush to 512 x 384 x 384 and position it against the floor in the middle of the room (like below) and subtract it. You should now have a huge pit in the middle of the floor.

RIGHT CLICK THE SHEET BUTTON AND SET THE SHEET SIZE TO 384 X 384 AND MAKE SURE FLOOR/EILING IS SELECTED. POSITION THE SHEET SO THAT IT FITS PERFECTLY OVER THE OPENING OF THE PIT AND JUST BELOW THE EDGE (SEE BELOW).

SELECT A NICE LOOKING WATER TEXTURE FROM LIQUIDS.UTX AND SELECT IT (MAKING SURE NO TEXTURES ARE SELECTED IN YOUR MAP). NOW CLICK ADD SPECIAL BRUSH AND FROM THE DROP DOWN MENU CHOOSE WATER (AND MAKE SURE ALL THE BUTTONS ARE SELECTED LIKE BELOW).

ADD THE SPECIAL BRUSH. NOW ADD A LIGHT AND A PLAYERSTART AND YOU SHOULD END UP WITH SOMETHING LIKE THIS:

NOW, IN THE BROWSER, GO TO CLASSES, INFO, ZONEINFO, AND SELECT WATERZONEINFO. ADD A WATERZONEINFO INSIDE THE AREA WHERE THE WATER WILL BE.

REBUILD YOUR LEVEL, SAVE IT IF YOU LIKE AND TEST IT. YOU SHOULD HAVE A NICE LITTLE POOL OF WATER IN THE CENTER OF YOUR ROOM. YOU CAN APPLY THIS TO ANY SIZE, SHAPE WATER ZONE. JUST MAKE THE AREA WHERE THE WATER IS TO BE, ZONE IT OFF, GIVING A WATER TEXTURE TO THE SURFACE OF THE WATER, ADD A WATERZONEINFO. THAT'S ALL THERE IS TO IT. YOU CAN NOW ADD EFFECTS TO THE WATER TEXTURE LIKE SMALL WAVY OR BIG WAVY TO GIVE IT MORE MOTION IF YOU LIKE.

NOTE: Some people will "intersect" their water sheet when adding it to an odd shaped pool. I have had much better luck (not to mention fewer BSP errors) by making the water sheet a square, but big enough to cover the opening of the pool and letting the rest "overhang."

Waterfalls

Waterfalls aren't that difficult to make. Making them look good takes practice. Use pictures of waterfalls as a guide whenever possible. I will teach you the basics on making them, but you will have to practice to make the "right."

Okay. Kill the old map (or save it if you like) and start a new one. Again, start with a 512 x 512 x 512 room. Subtract it. Now make your brush 256 x 256 x 512 and align on the floor as we did the pool. Subtract it and you should have a trench like below.

Now resize the brush to 512 x 256 x 32. Align it against one wall as shown below, then intersect it and then subtract it.

Now add water sheets (using the sheet tool and special water brush) to the trench (just like we did the pool) and the cut we made in the wall. You should end up with something like this:

Now add a WaterZoneInfo in the trench. Add light(s) and a PlayerStart and rebuild. You can go test the map if you like now. The water fall is there but sure doesn't look real does it? To make it look like the water is actually falling we need to set some texture properties. Select the waterfall texture and bring up it's properties. First go to the Scale tab and under Custom Scaling leave the U the same and set the V to something about 8ish.

Now go to the Effects tab and check V-Pan.

Note: Look at the water fall in the 3D view (making sure the little joystick icon is clicked in that view). Your water fall may appear to be falling "upwards." If this is true then got to the Rotate tab and click FlipV.

Your water should appear to be falling now. You can also set the v-pan effect on the "stream" to make it appear to flow.

Tips

The water fall we made works, but is not very realistic. First of all the water doesn't come from anywhere in particular and doesn't go anywhere. If we wanted this to appear real, we could make a ledge above for the water to be falling from and make an opening on the opposite wall for the water to be flowing into.

These are the kinds of things I was talking about when I said you would have to practice to make it right. There are several water textures in Unreal. Look around the different .utx files and find ones you like (GenFluid, Liquids, FireEng, HubEffects, etc...). Some work better than others for water falls.

Slime & Lava

Slime and lava are made the exact same way as water. just insert the proper ZoneInfo and select textures to match.

Note: A lot of people have been asking about rising water effects, to flood a room or something. You can't do this with the Unreal Engine without writing a script for it. You can make a moving water sheet, but not the zone portal. Zone portals are stationary in Unreal and cannot move. You can make it LOOK like water is rising or falling (using a moving sheet textured like water) but you have to keep it shallow enough that you cannot go under to find that it is not actually water at all.

Also, waterfalls should be made narrow enough that you cannot swim up them. If you have a water fall that is part of a water zone and it is wide enough (32 units I believe) you can "swim" up the water fall. This might be a neat trick in certain levels, but not very realistic.

If you test your level after adding water and the whole room is filled with water (not just "under" the water brush) then you have a zone portal leak somewhere. Check your sheets and delete and re add them if you have to. Protruding objects: You can make things sticking up out of the water, but you have to add them BEFORE you add the water sheet. You may stuff under the water after you add the water sheet (zone portal) as long as it doesn't touch the water sheet.

Using what you have learned

Play around with water and have fun. Try different things and see what looks good and what does and doesn't work. As always, if you have any questions e-mail me.

-Wolf

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Special Effects 1

Wolf's Tutorials Part 9 -- Special Effects Part One

Fire, Smoke & Fog

In this tutorial we are going to cover such things as Fog, Fire, and Smoke. Some of the really cool special effects that set Unreal apart from other FPS games.

Ready . . . Aim . . . Fire!

Everyone wants fire in their map at some point. Here is how it's done. There are actually several ways to make and several types of fire. We are going to concentrate on the two easiest to get you started. The first is a TorchFlame and the second is a simple masked texture fire.

TorchFlame

Too easy...gimme something harder..oh, alright..here's a simple torch: Make a standard 256 x 512 x 512 room. Pick some pretty textures and subtract the room. Now, we need somewhere to place the flame, so build a 64 x 64 x 64 box in the room. Texture it as you see fit. In the Browser go to Classes, Light and select TorchFlame. You are probably way ahead of me, but right click the top of the box we made and Add a TorchFlame actor. Center it on top of the box. Rebuild. You can adjust the light properties of the TorchFlame Actor just like any other light.

Since the 220 build, the TorchFlames are much bigger than they used to be. You can adjust the size of the TorchFlame by right clicking it to bring up it's properties and under Display setting the DrawScale. I usually use .5 myself. One is normal size, .5 half, etc...
Note: This does not affect the light out put, just the size of the flames.

Sheet Flames

You can make other types of fire using masked sheets. Load the GreatFire.utx texture pack and look at some of the different flames. We are going to use these textures.

First let's build something for the flame to sit on. In the same room make a square 32 x 128 x 128. Texture it and add it to the world. Now select a nice fire texture from GreatFire.utx like AncFlame1. Open the Sheet properties, click X-Wall and make it 128 x 128. Position the sheet on top of and in the center of the little box we just added. Click Add Special and go to Masked Wall. Click 2-sided! Now add the The special brush, and you should have a flame atop your little dais. Click the flame and open the texture properties and set Unlit. Add a PlayerStart and then rebuild. Run the map. The flame looks ok on there but when you look at it from the side, you can tell it's a flat sheet! Take your red brush and rotate it 90 degrees so it forms an X with the first sheet and add another flame sheet like the first one (making sure to make it Unlit).

Now rebuild and run it again. Ah...better.

Note: To me this type of making a flame looks much less real than just adding a TorchFlame, plus it doesn't give off any light of it's own. I usually only use sheet fire in recesses in the wall where they are only visible from one side, or in other "special" situations.

Let's build another sheet flame that looks better. Build a niche in one wall 128 x 128 x 32. Align it in the center of the wall, a few units off the floor and subtract it.

Now select a fire texture like AncHot. Add a sheet (just like we did before) in the center of the niche.

Make sure the texture on the flame sheet is unlit. now add alight in front of the flame and give it a reddish color.

We can also add a grate in front of the fire. load the NailCast.utx textures. Go to Mask and select one that 128 x 128. using the same sheet brush we just added the last flame with, move it to the edge of the niche where we added the flame. Leave all the properties the same and Add Special. You should now have a masked grate in front of the flame.

Note: This grate will not block anything (gunfire, players, bots...). If you wanted to block them you could add an Invisible Collision Hull. To do that select the cube brush and make it 126 x 126 x 1. Yes, ONE unit thick. Right click on the GRID and change the grid setting to 2 units. Now move the grate sheet back into the niche one one "snap" or two units. Place the new cube brush at the edge of the niche, but not touching it. Note: We made the "grate" smaller than the opening because if the masked brush (the grate in this case) touches the subtracted wall we will get a HOM or Hall-Of-Mirrors effect there. If you don't know what I am talking about then make the grate the same size as the opening, add it and then run the map.

Now, under Add Special, choose Invisible Collusion Hull from the drop down menu and add the brush. It should be invisible in the 3d view. If you click the little joystick icon it will appear.

The thing I don't like about collision hulls is that it adds an extra step. If you made the grate out of the 1 unit thick brush you would get the same results. Delete the grate brush and the collision hull. Now move the brush we used for the collision hull where the grate sheet used to me. Select the grate texture and Add Special Masked Wall. Now rebuild the map and run it. Works good, huh? And we eliminated a whole step (adding the grate sheet).

Note: The whole reason you have to either add collision hulls or make the one unit thick brush is because a sheet will NEVER block actors, no matter how you set it. There is a lot of confusion on this issue, but the fact is a SHEET WILL NEVER BLOCK ACTORS!

Smoke

Where there's fire there's smoke...or there should be. To are (as in a lot of cases with UnrealEd) several ways to make smoke. One way it to find a cool smoke texture and add a sheet just as we did with the flames. Another way would be to use the Smoke special effects from the classes menu. Since you make the smoke sheet exactly as you did the flame sheet, I'll show you the Special Effect method.

We'll keep using the same map we have been working on. Let's make a new room and then connect it to the first room. Make a small hallway first, let's say 256 x 256 x 256. Place it in the middle of one of the bare walls, against the floor then subtract it and texture it. Now make your brush 512 x 512 x 512 and add a room on the other side of the hall. Your map should look something like this now.

We are going to add another TorchFlame in this new room and then add some smoke over the TorchFlame. First go back to the first room and click on the block the TorchFlame is sitting on to highlight it. Now right-click and Copy Polygons To Brush. Click the grid to unhighlight the brush and move the red brush to the center of the new room on the floor, and add it.
Add a TorchFlame to the block as we did before. Remember to resize it so it fits correctly (or you can copy the first torch flame by highlighting and clicking CTRL-W and moving the new TorchFlame into place). Now open the browser, go to Classes, and then Effects. See the two classes titled SmokeHose and SmokeHoseDest? We are going to use those.
The first one, SmokeHose, is where the smoke will generate, and the second SmokeHoseDest is where it is going to. We need to add the SmokeHose just over the flame and the SmokeHoseDest near the ceiling. Go ahead and do that now.

Rebuild and run the map. You should see a little trail of smoke rising up from the flame to the ceiling. Easy, huh? Now, I wouldn't use this on every flame in your level, as I am sure lots of these will slow down the map, but you can use it occasionally to get the desired effect.
You can adjust various settings in the SmokeHose Properties to change things like the size of the smoke puffs, the speed they rise, the variation of the individual puffs, etc . . . Experiment with the settings to see what kind of things you can do with it.

Smoke Generator

Another way to make smoke is using the SmokeGenerator class under Effects. The only thing about smoke geneartors is that they have to be triggered to work.
Okay, go to the SmokeHose & Dest we added and delete them. Instead, put a SmokeGenerator above the TorchFlame. See that nice puff of smoke? That is where the smoke will enimate from. Clcik it and bring up it's properties. We only need to set a couple things to get it working. One is give it a tag under Events. I used "Smoke1" to keep it simple. Under Smoke Generator, set bRepeating to true. Now add a trigger right by your player start. Set it's event to "Smoke1" Rebuild and run the map.

Tips: You can play with settings under SmokeGeneartor heading like BasePuffSize, RisingVelocity, etc... to get various effects. I usually use SmokeGenerators for regular, rising smoke and normal fire smoke, and use SmokeHose for "special" situations.

Fog

One of the most frequently asked questions by a new UnrealEd users is "How do I make fog?" This tutorial will show you how, and give you some tips on how to do it so it looks good.

Like so many things in UnrealEd, fog is made by using a zone. Basically all you need is a zone, a ZoneInfo, and a light. Light is the key. The ZoneInfo tells the game engine "here, there be fog..." and the light "shows" you that fog.

Okay, start by making a small hallway coming of the last room we made. make it 128 x 128 x 256. Now make the brush 128 x 128 x 512 and connect it to the last hallway at a 90 degree angle, making an "L."

Now make a room 512 sq. Add it at the end of the hallway like this:

Now we need to "zone off" the fog area. To do that make a sheet brush big enough to cover the hallway opening (in this case 128 x 128) and place it in the hall. Place it like below:

Now go to Add Special Brush and from the drop down menu select Zone Portal and then Add Special Brush. Now rebuild and you will see that the light from the last room does cross the Zone Portal normally.
The Zone Portal is invisible and cannot block actors because it is a sheet. Note: If you change the 3D view to Zone/Portal View (under Mode) you should see that the area on one of side of the ZonePortal is one color and the area on the other side a different color. If this is NOT true, then you have not placed the ZonePortal properly. Delete it and try again.
Tip: The Zone/Portal View is an excellent way to check your zones integrity. you can check ALL zones this way.

Now we have to make the new zone a fog zone. Go to Classes, Info, and add a ZoneInfo somewhere in the last room we made (512 sq). If you expanded the ZoneInfo you didn't see a FogZone did you? There isn't one. Just use the generic ZoneInfo and we will set it to a FogZone.
Highlight the ZoneInfo and open it's properties. Expand ZoneInfo and the second field down should be bFogZone. Set this to, you guessed it, true. Add a light in the center of the room. Open the LightProperties and expand Lighting. See the three "Volume" settings at the bottom (VolumeBrightness, VolumeFog, VolumeRadius)? These are the setting that make the fog appear. Try setting them all to 32 except VolumeBrightness--leave it at 64. Rebuild and run the map.

You should now have fog in the last room. Try changing the Volume settings under the light Properties and see how it changes the fog.

Note: The reason we built our hallway/Room like we did and place the ZonePortal where we did is because fog is NOT visible from zone to zone. If you have a straight hallway that leads into a fog zone the fog will not be visible until you enter the fog zone, making it suddenly "appear" as you cross the ZonePortal. So try to make your fog zone in an area that is not visible from the previous zone.

More Special Effects will be covered in Part Two of the Special Effects Tutorial.

Using what you have learned

Play around with these effects and have fun. Try different things and see what looks good and what does and doesn't work. As always, if you have any questions e-mail me.

-Wolf

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Portals

Wolf's Tutorials Part 10 -- Portals (Warp Zones)

Portals

Portals are essentially zones that not only teleport you from point A to point B, but allow you to SEE where you are going to. The portal is a "window" to another place. Since they are actually zones which "connect" the actual terminology would be Warp Zone. In this tutorial the term "Warp Zone" will refer to the portal structure. The term "portal" will refer to the actual zone sheet which is the "window" to the warp destination. You can build a simple "doorway" portal, or a much better effect is a "closet" that you can walk all the way around, or walk into and "warp" to another area (see DmRadikus).

Ingredients

For a warp zone to work you need two warp zones. The two zones have to be the exact same size and the warp zone structure can only have one portal (opening). It will actually function with two open sides, but the effect this has on the portal is weird to say the least.

Portal Structures

In this tutorial we are going to build two unattached rooms. We are going to build two portals and we will discuss some options when building portals. To start with pick a texture set and build a starting room 256 x 512 x 512. Texture it anyway you like. Now highlight the room brush, and right-click. Choose "Copy Polygons to Brush" and then deselect the room brush. Move the red brush to a another location and subtract it. You should now have two identical rooms. Change the textures in one so they don't look like alike. Now build a work room 512 x 512 x 512 off to the side. We will build the portal structure in this work room.

Now we have to decide how big our portal "closet" will be. We know our two rooms are 256 high, so it should be taller than that. Let's make the portal itself 128 x 128. Now the structure or closet this portal will sit in must be slightly larger. We want to allow for a lip of 16 units on each side, so we must make the structure 32 units larger than the portal--16 (lip) x 2(two sides)=32 units. Select the cube tool, and go to properties and size it to 160 x 160 x 160 (or 128+32). Make sure your red brush is in the middle of the work room and then add it to the world. This block is the "closet" for the portal so pick a nice texture for it. Now resize the red brush to 128 x 128 x 128 and position it so it "touches" one side of the block. See below:

Now subtract the brush. This should "hollow out" most of the block, leaving one side open. Resize the red brush so that it completely surrounds the block and then intersect it. Move the new red brush to the first room we made and place it in the center of the room. Add this brush. Now move the red brush to the center of the second room and add it.

Now we have our "closets" build we need to turn them into zones, and then warp zones. To zone them we need to add zone portals over the opening to "seal" them. Select the sheet brush tool and make it 128 x 128 (the size of the opening in the block), and then select X-wall or Y-wall depending on which way your opening faces. Now move the brush to the opening on the block of one of the portals and make sure it is centered. Leave a lip so it is not right on the edge of the opening, but a little bit back (I resized the grid to 8 units and moved it back one "snap" or 8 units).

When it covers the opening then click the Add Special Brush tool, and from the "Predefined" menu select "Zone Portal" and then add this brush. Move the red brush to the next portal (in the other room) and repeat. You can check to make sure you have the zones correct now by Rebuilding and then switching the 3D view to Zone/Portal view and making sure you have a different color inside the block.

Now that we have the portals zoned we need to define them as Warp Zones and set their properties so the warp to each other. In the Browser select Classes. Go to Info/ZoneInfo and highlight WarpZoneInfo. Put a WarpZoneInfo inside each of the portals. Go to the first WarpZoneInfo you added and open it's properties dialog. You will see a heading titled (oddly enough) "WarpZoneInfo." Expand this and look at the last two fields:

OtherSideURL --The "name" of the destination portal ThisTag --the "name" of this portal.

Name one Portal Warp1 and the other Warp2, that is set the above fields to Warp1 and Warp2 in one WarpZoneInfo and to Warp2 and Warp1 in the other. Now add some lights in each room and a player start in one. Rebuild and run the map. You should now be able to walk all around the portal structures and see from one room to the next. You should be able to walk through the portal and "warp" to the other room.

Tips:

If you should get a HOM (hall of mirrors) effect or other strangeness in your portals, try deleting the zone portal sheets, rebuilding, and then adding them in again, making sure they are lined up correctly. Portals are fairly easy to make, but can be kind of "touchy." Just remember to make the warp zones the same size and make sure the sheets are lined up correctly, and that you have the WarpZoneInfo properties correct and they should work fine.

I have included an example map with this tutorial for you to examine (incase something doesn't work right--I told you portals are touchy!).

Using what you have learned

Play around with these Portals and have fun. Try different things and see what works doesn't work. Try making different shaped portals in different locations (one that comes out near the ceiling, etc...). As always, if you have any questions e-mail me.

-Wolf

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Eleven - Triggers

Wolf's Tutorial #11 - Triggers

Introduction:

This is an intermediate to advanced tutorial. If you are new to UnrealED then please see my tutorial page for links to Basic Tutorials to get you started.

Triggers

We are going to discuss (and learn to use) various kinds of triggers in this tutorial. We will not cover all triggers. This tutorial is designed to get you familiar with how triggers work and their various properties. You will learn how they work and a few different type of triggers you can deploy for various effects. You can use what you learn here to figure out other kinds of triggers.

Basic Trigger

For a trigger to work you need a couple basic things. An Event and a Trigger. There are two properties that are ESSENTIAL for triggers. They are found under Events (any any actor Properties window). They are Event and Tag.
You can think of the Tag as the name of the actor. This Tag is the "name" the trigger calls to send the actor into action.
The Event is the where you put the Tag you want the trigger to call.

Simple Example

Switchable Light
Let's start by making a small room (256 sq). Add a PlayerStart in one corner. In another corner add an object that might be a switch. Add a trigger (Expand Triggers and choose the Trigger with the big minus sign next to it --see image below) in the corner in front of your switch looking texture.

Now in the Browser go to Classes. Expand Light and choose TriggerLight (the first one). Now add the TriggerLight in the center of the room, near the top.
Now we have all the actors, we just need to set it up.
First, set the Tag (under Events) of the light to something unique, like LIGHT1. Now open the trigger properties and under Events set it's Event to LIGHT1 (or whatever tag you gave the light). Now wee need to do a couple more things to the light. First open the TriggerLight properties window. Under TriggerLight make sure bInitiallyOn is set TRUE.

Now under Object/InitialState set it to Trigger Toggle. This will turn the light on or off as the trigger is activated.
Okay, that should do it. Now Rebuild and then run the map. When you walk to the corner where the trigger is the light should go off. Take a couple steps back and then forward again and it should come back on. Basically, whenever you activate the trigger it will toggle the light to on or off.
Breakdown:
The EVENT in the trigger properties tells the trigger WHAT to activate. The TAG of the light names it so the trigger can activate it. Always make sure the Tag of the actor you want activated and the Event of the trigger you want to activate it are the same. The light property set to initial on (bInitiallyon=True) can be set to off and then the trigger would turn it on). The Object/InitialState of the light is set to toggle so that it will switch back and forth between on and off each time you activate it.
Explore:
Try setting the InitialState of the light to other settings besides TriggerToggle.
Try TriggerControl: This should make the light go out whenever you are in proximity of the trigger.
TriggerTurnsOn and TriggerTurnsOff are pretty self explanatory.
TriggerPound doesn't work on a TriggerLight actor.

Trigger Properties

So, all the settings that control the light we set with the light actor. Doesn't the Trigger itself have any properties we can set? Glad you asked.
Yes.
There are a few things with the trigger you can set as well. Open the trigger Properties then Expand Trigger.

TriggerTypes
Look at the TriggerType

This defines what activates the trigger. TT_ means TriggerType to the engine.
The default is TT_PlayerProximity. This simply means whenever the Player is within the Proximity of the trigger--see Proximity below).
TT_PawnProximity means only pawns (any pawns) can activate the trigger.
TT_Classroximity means only a special class (defined in ClassProximityType) can activate the trigger.
TT_AnyProximity means any of the TT_ types can activate the trigger.
TT_Shoot means you have to shoot the trigger to activate it.
Proximity
This is how big the the "area" of the trigger is. You set this under Collision/CollisionHeight and CollisionRadius. These should be pretty self explanatory.
InitialState

Under Object/InitialState you can set the "state" of the trigger. Your choices are None, Normal, OtherTriggerTurnsOn (or Off), OtherTriggerToggles.
Normal is just that. The trigger works when activated.
None will deactivate the trigger altogether.
OtherTrigger settings should be pretty evident what they do.

Trigger Settings
Under Trigger (where we found the trigger types) you will see a few more settings that you can adjust.

bInitiallyActive A True/False toggle. Setting it to False means something must activate the trigger (another trigger for example) for it to work.
bTriggerOnceOnly A True/False toggle. If set True it will only work ONE time.
ClassProximityType Used to define the ClassProximityType under TriggerType
DamageThreshold How much damage you must inflict on the trigger when it's type is set to TT_Shoot.
Message The message to display when trigger is activated.
RepeatTriggerTime Time between the trigger repeats
ReTriggerDelay Time delay before trigger can be re-activated.
TriggerType (see TriggerTypes above).

Other Triggers Classes
If you expanded the Triggers tree in the browser (which you had to to find Triggers) then you saw lots of other kinds of triggers. I will not explain them all but here are some popular kinds you may want to try out. An (!) means It is defined in more detail below:

CodeTrigger (see ElevatorTrigger) Counter Must be triggered a certain number of times before sending the trigger info to it's event
Dispatcher (!) Is triggered itself then activates a user set sequence of events with optional delays
ElevatorTrigger Hmmm...I wonder what this does? I'm saving this for a future tutorial!
FatnessTrigger When activated sets the fatness of an actor from one state to another.
FearSpot Creatures will tend to back away from this area.
Jumper Creatures will jump in a specified direction when activating this trigger
MusicEvent (!) Where you specify and change background music
RoundRobin Everytime you trigger this it advances through a set of outgoing events
SpecialEvent (!) Receives a trigger message and sends out various "Special Events" depending on it's setting
StochasticTrigger Kind of like a dispatcher but you set certain special events to occur
TranslatorEvent (!) Where you set the "pop up" Translator Messages
-Trigger Basic Trigger
ZoneTrigger Activates/deactivates zone properties (only bGravityZone works).
TriggeredDeath Kills that which activates it.

Dispatcher

Dispatchers are used to trigger multiple events. You can delay the out put to create a "scripted sequence" of events.
You can use it to do all sorts of things from one trigger with various delays. You could have it open a door, turn off (or on) a light and play a sound then wait a second and shut the door, toggle the light and play a different sound. You can do lots of cool "scripted events" with these handy triggers!

OutEvents set the sequence of events you want triggered and in what order.
OutDelays set the delay between the events.

MusicEvent

Here is where you set and change the cool Unreal background music.

Song and SongSection are the two settings you will use the most Song picks the song you want to play (under Music in the Browser you can load the songs you want) SongSection is a setting of 0, 1, or 2 to set the mood of the song (normal, tense, action I think)

SpecialEvent

The settings under Object/InitialState determine what type of special event is triggered:

For the most part they are self explanatory (PlaySoundEffect, DisplayMessage, etc...) The particular settings for each type are set under SpecialEvent.

TranslatorEvent

This is where you trigger the messages that appear on the Translator.
The specific settings and the Message itself are entered under TranslatorEvent.

Other Ways to Trigger Events

Actor Triggered Events

There are other ways to trigger events besides actual triggers. The easiest and most common would be to set an Event to an actor such as a mover. If you specify another actor's Tag in the Event of the mover, when it is finished going through it's key frames it will activate the actor. For instance, let's say you have a door that is activated by a lever. Make a mover that looks like a lever. Make a door. Give the door a tag like "Door1" and set it's initial state to TriggerOpentimed. Now set the event of the Lever to "Door1" and when the lever moves it will activate the door.

BumpButtons

Bumpbuttons are cool ways to set one mover to activate another and not have all those red lines connecting them in your maps.
Make a mover that looks like a button (or whatever you like). Make another mover that this will trigger (like a door or something). Now give the door a unique tag.
Now under the button's InitialState set it to BumpButton.

Now under Mover set the Bump Event to the tag of the "door" you want it to activate. You can also set the BumpType (to player, pawn or any).

Now if the button is touched it will activate the other mover. You can also set the BumpEvent to a SpecialEvent's tag and activate a special sound or message or whatever. You can do lots of cool things with this one, too.

Tips

Anytime you want a single trigger to trigger multiple events use a dispatcher.
Anytime you want a "button" or "switch" (a mover) to activate something set it to a BumpButton.
Always double check your Tags and Events if something isn't working right. This is usually not set right when something fails.
You can check just what properties a trigger is affected by simply by opening the trigger's script (double-clicking it in the browser) and reading the comments (green stuff).
The way the trigger affects the event (toggle, control, etc,...) is set in the actor's Object/InitialState.
The way a trigger is activated is set under the Triggers TriggerType.

Pull The Trigger

Now you should have enough info to start triggering things all over your level. You should be able to trigger whatever you like now and set it off the way you want. Explore and play around with different types of triggers and their settings.

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


Wolf's Tutorial Twelve - Sound and Music

Wolf's Tutorial #12 - Sound and Music

Introduction:

This is an intermediate to advanced tutorial. If you are new to UnrealED then please see my tutorial page for links to Basic Tutorials to get you started.

Sound and Music

So, you think you're level is just a bit too quiet? Unsettling isn't it. Sound can make or break the atmosphere of a level. This tutorial will show you how to create ambient sounds, triggered sounds and music for your Unreal levels. Lights, sound and music can really create a tangible atmosphere for your level. This tutorial will NOT cover lights so you will have to wait for that tutorial, but You will learn the basics of implementing sound and music.

Getting started

To start with build a room about 256 x 512 x 512. Size doesn't matter nor do the textures. Add a light and a PlayerStart. Now build a hallway coming off the room and make it 512 long. It doesn't matter what direction it runs. Add a light in the hall. Now make an identical hall and attach it to the end of the first hall at 90 degrees so it makes a nice "L" shape. Now add another hall at the end of the second one, again at 90 degrees making another "L" At the end of the last leg add another room the size of the first. Add lights in all the rooms and all the halls. You should have something like this:

Ambient Sounds

Ambient sounds are those "background" noises that play in your levels. It might be the sound of a fan or machine running in the background, or a drip from a leaky pipe, or the wind howling in an outdoor area. Ambient sounds can really set the tone for your level. Imagine your player is in a huge engine room and there is no sound. Hmm...seems boring and unrealistic. Now add an ambient sound of an engine hum and BAM! The room becomes real! You feel like you are there. You need two things to make an ambient sound in your level. The ambient sound actor and the sound file it will play.
In the Browser choose Classes and expand KeyPoint. The first item under KeyPoint is *AmbientSound. Click that to highlightt it. Now pick a corner in your starting room and right click and choose Add AmbientSound here. You should get a little icon like this:

That is where your ambient sound will emanate from. Now we need a sound! In the Browser choose SoundFX. We now have to load a sound package. If you click on the Load button at the bottom you will get a window with lots of sound package files with the .UAX extension. There are lots of sounds in each package file. Choose one of the Ambient packages like AmbModern. You will notice that there are two sub menus for AmbModern: Looping and OneShot. They should be self-explanatory, but Looping will play and replay forever and one-shot is a single sound played once. For an ambient sound ( or background noise) you'll want a looping sound so choose Looping.

There are several looping sounds in the menu now. Click on one and press the Play button at the bottom. when you have heard enough press Stop. Find a nice ambient sound you like and highlightt it. I chose "comp1" for no particular reason. Now right-click your AmbientSound actor and open it's properties. At the bottom, expand Sound. Click on AmbientSound and then click Use and then the "..." button. A file name should appear in the field now. If it doesn't go back and highlightt the sound you want again in the browser and then try again. Your properties dialog should resemble this:

You should also hear the sound playing now. If you don't click the update real-time button in the 3D window (it looks like a little joystick). Note: this button will toggle sounds and texture animations, as well as showing invisible portals). You now have an ambient sound in your level! Let's give it a source. Go back to the browser and choose textures and load PlayrShp. Under Panel scroll down until you see a computer screen texture and highlightt it. Now build a brush 256 x 64 x 256 and place it against the wall. Add it to the world. You will want to change the side textures but leave the computer screens on the "front." Move the AmbientSound actor in front of the brush like below:

Now rebuild and run your level. The ambient sound of the computer noises should be playing and it should seem to be coming from the computer screens we added. As you walk away the sound should diminish and get louder as you approach the "computer." If something isn't right or you hear no sound go back re-trace the steps above. If you did everything right you will get sound (assuming the sound works on your computer!).

Triggering a Sound

Okay, we have ambient sounds now, but suppose I want a sound to play when a player does a certain action. How can I trigger it? Glad you asked, young apprentice! Sounds can be triggered and here is how:

You need 3 things to trigger a sound:

  • A SpecialEvent actor
  • A trigger
  • A sound (obviously)

First you need to decide where and what sound you want to trigger. As for "where" let's have it triggered as the player leaves the room.
In the Browser choose Classes and expand Triggers. highlightt (but don't expand) -*Trigger. Now right click at the entrance to the first hallway and add a trigger there. under the Trigger Properties, set an Event. Call it something cool and original like "sound1" without the quotes. Now in the Browser under triggers highlightt *SpecialEvent. We want the sound to come from behind the player as he leaves the room so add the SpecialEvent actor back in the start room. I chose the opposite corner from the "computer" we added earlier.
The SpecialEvent actor looks like a trigger with an ! on it. Set the Tag (under Events) of the SpecialEvent the same as the Event of the trigger (sound1 in the example above).

Now we need a sound. In the Browser choose SoundFX again and select AmbModern. This will be a OneShot sound. Choose "scream1" and highlightt it. Bring up the SpecialEvent Properties and expand Object. Set the InitialState to PlaySoundEffect.

Now expand SpecialEvent, highlightt Sound and click Use and the "..." button.

Rebuild and run your level. Now when the level starts you should hear the computer, and when you enter the hallway a scream should come from behind. You can trigger any ambient sound with this method. If you trigger a Looping sound it will play forever. This can be useful if you want a certain looping sound to start only after the player triggers it (like turning on an engine or something). The scream should play everytime you walk through the trigger. In a REAL level you would probably only want it to play once so you would set the Trigger property bTriggerOnceOnly to TRUE (under Trigger). There are times, however when you want a one-shot to play everytime it is triggered, for instance, if you had a wooden floor you wanted to creak everytime someone stepped on it.

Expanding Your Sound Control

Okay, we can make ambient sounds, we can trigger and ambient sound, we can trigger a one-shot sound. What else can we do? In the AmbientSound properties you will notice other fields like, SoundPitch, Radius and Volume. If you don't know what these do then play with the setting to discover. I could tell you but that would spoil the sense of discovery for you. :)

The Sound of Music

Someone I like and respect said to me: "If you're going to do a sound tutorial you might as well cover music, too." that is not an exact quote but that is basically what he said. So here goes: Music is too cool in a level and essential for a good single player level. The music in Unreal is cool and you can implement it very easily. In the Browser choose Music (oddly enough) and then Load. You will see the various music files that came with the game Unreal. You will recognize most of these. Load some into the Browser, highlight it and click Play to sample some. When you are through playing click Stop. Pick one you like. I chose EverSmoke because I like it.
To make Eversmoke the music for your level is too easy. Press F6 to open the Level Properties menu. Expand Audio.

Now make sure your music selection is highlightted in the Browser and highlightt Song and then click Use and "..." and your properties dialog should now have the Eversmoke file udner Song.

Now rebuild and run the level and when you start the music should be playing. Cool and easy, huh?

Changing Music

"But Wolf, after a coule minutes I'm sick of EverSmoke. What do i do?" You can change the music anytime in a level by adding a MusicEvent. In the Browser choose Classes and expand Triggers and highlightt *MusicEvent. Go to the end of your first hall in the 3D view and add a MusicEvent there. Give it a unique Tag (under Events) like "musicswitch." Choose Music in the Browser and load a new music file. highlightt it and then open the MusicEvent Properties. Under MusicEvent highlightt Song, click Use and "..." and the new song should appear in the field.

Now add a Trigger and under Events set it's Event to "musicswitch." Rebuild and run and now when you get to the end of the hall (the trigger) the music should change.

Music Mood

You can also use the music event to change the "mood" of the music. This can be a wonderful effect, especially in SinglPlayer levels. You do it by adding a MusicEvent and triggering it the way we did above. Choose the same song you started with and under SongSection selct 0 1 or 2. The slider goes to 255 but that doesn't matter. I beilieve there are 3 settings

  • 0 = base song
  • 1 = Tense/dramatic
  • 2 = action

Now, I may be wrong on those but try them and you sill see the difference. As you progress down the hall add MusicEvents and Triggers to change the music. Set them kind of far apart so you can notice the change. You can see that implementing these at key points and times in your level can truly enhance the player's experience. You can also Turn off the music with a music event by clearing the Song field and setting bSilence to True.
Try it. after adding the Song sectin changes, at the end of your halls add one to kill the sound.

Using Music and Sound

I can't stress enough how much music and sound can affect your levels. The right music at the right time can get a player's juices flowing and set the tone for the level and events going on around him. The placement and proper use of sound effects can make the atmosphere of your levels seem realistic and can totlly immerse a player in your level. Music and sound are essential parts of any good level and should be used wisely. too many music changes can distract the player and too many sound effects at once can be annoying. If something should only trigger once (scream, explosions, etc...) make sure they only trigger once. If something should make noise add an ambient sound near it and choose the proper sound for the experience (don't make a computer sound like bongo drums and and don't make electric sounds in ancient temples).

Sound and music can add a new dimension to your levels.

Good luck, have fun and as always, if you need further help, let me know.

-Wolf

©Copyright 1999 Tony Garcia (a.k.a. Wolf). No part of this tutorial may be reproduced without my permission.

Wolf's Unreal


2D Editor Basics

    Chris Willis
    chris.willis@sierra.com

    1.

    Once in UnrealED pull down the "Window Tab" and goto the " 2D editor "

    2.

    After you've opened it you will see a blue triangle, each side of the triangle can be edited by pulling on the corners of the triangle or by inserting a vertex under the " Vertex Tab ". Each vertex inserted can be moved to make the shape more interesting. Remember: When you use this tutorial think of the object as one half of the whole object you want to make because you will be revolving it, not lofting it. The shape being made will rotate around the green crosshair so the crosshair needs to be at what will be the imaginary center of the finished shape ( the green crosshair needs to be on the lefthand side of the object, ok? ). :)

    3.

    Goto the "Loft Tab " then to " Revolver ". There you'll be able to choose how many sides you want your shape to have. I used the default of " 6 ".

    4.

    Hit Build ,and there you go! Your custom shape is made.


Complex Brushes

Complex Brush Shapes

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    This tutorial will deal more with the actual creation of brushes then with the implemenation. Using a brush is easy, but actually creating one is a little more difficult. This tutorial will show you how to create a simple table into one brush. I will not be telling exact sizes or anything like that, but you will be able to follow quite easily.

    The Table Top : This will be the easy part. All you have to do is to create a simple cube with the size of the table that you want. Make it either square or rectangular if you like, but make sure that it is not to think, otherwise the table will look akward.

    The Legs : The legs of the table are another simply thing to do (Do you see why I chose a table for an example now? Hehee) Make a brush that is the shape of a long peice of wood. use something that will sit under your table and then allow the table to "rest" on it when it is complete. When you choose how thick, and how high you want your table legs to be, an them in. I will be using pictures here from a table that has got six legs and is pretty close to the ground, but that is merely an example.

    The Edges : You could leave your table at that, but why not add in some edges for the underside of the table to add in that little "extra" and make the table look once more that little more realistic. Make a brush in the shape of a basic plank of wood, then place it so that each end will reach a set of legs, but no poke through into the other side of the leg. If you find that you brush is too long, don't "scale" the brush, but change the actual length of the cude that you are using. If you scaled the brush, it would later effect the textures on the brush, and that would look poor when you actually used it in a level later on. When you have got the plank in between two legs, just under the table top, intersect the brush, and then add it into the world. Repeat this process until you have got the entire underside of the table with an adge.

    When you have done the steps that are written above, you will be able to create one brush from the entire table. To do this, create a brush from one of the primative (anything, but most likely a cube brush) and make it large enough to "surround" the table entirely. (For example if you table is roughly 200 wide, 150 long, 64 high, then make a brush that is 220 x 170 x 80) and place it so that the table does not protrude from any point within the vube. Now simply press the intersect button and it will change your red builder brush into the shape of the table. You could use this brush straight away, but I would suggest placing it off to the side, and making a table there that will not be used at all, so that when you go to use the brush again, you will be able to do a simly "copy polygons to brush" and then use it wherever you like.

    If you do create a nice complex brush (even if it is quite simple) please send it down to the site so that we can post it here and let everyone use them. If you would like to save your brush at this point, make sure that the red builder brush is the shape of you table (or whatever you built) choose the brush menu and select "export brush". This will then give you the option of exporting and saving the brush.


Advanced Doors

    Verxion
    jc83151@goodnet.com

    FORWORD:

    This is the fourth tutorial I have written, it is starting to feel like I know what I am doing. :-)

    ABSTRACT:

    This tutorial goes over doors. Not just simple doors. DOORS. Complicated doors with bells and whistles. We don't start there though. We make a simple door, and then adjust it over and over to make it more and more impressive. To give you an idea of what I am covering, I will start with a simple door that opens in one piece when you approach, and ends up with a door that has sound effects and moving parts. You can basically follow the tutorial until doors of the level you want are covered. :-)

    ASSUMPTIONS:

    This is an advanced tutorial. I assume you know quite a bit about the editor to be able to finish the tutorial. I will try to make the first stage or two of the tutorial easy enough that if you are new to the editor you should come away being able to make at least simple doors. But to finish the whole tutorial, you will need to know your way around the editor fairly well.

    TUTORIAL:

    PHASE ONE:

    First off, make a room at least 512x512x512, and place some textures onto it.

    caption: here, you see the initial room.

    Next, we need to make a wall across the room, such that we seperate the room into 2 rooms. Once we have made the wall, cut a rectangular hole in it such that it looks like a doorway.

    caption: here, you see the wall seperating the rooms, and the adjoining doorway.

    Now, in my case, I have made the depth of the doorwar especially thick to make the construction of the more complicated types of doors easier to see. If you are just making a simple door, you would want to make the wall and hence the doorway of a fairly small thickness.

    Next, you need to make a door. Since this door will be a "mover", we will build it up in the air, texture it, intersect it, copy the poly's to the brush, and place the mover. Was that a lot of steps? Well, the first time through, we will show all the steps, don't worry.

    caption: this is the door, suspended in mid air.

    Now, you need to intersect the door. You do this by making your builder brush larger than the door itself, WITHOUT scaling. If you scale it, your door will end up being hollow, and you will be able to walk through it. Right click on the block symbol in the toolbar and adjust the height, width, and breadth under "cube properties".

    caption: This is the door, surrounded by the builder brush.

    Now, intersect the door. Next, right click on your builder brush, and choose "copy polygons -> to brush". Now, move your builder brush to where the door should be when it is "shut". Once it is there, go ahead and delete the door suspended in mid air (or, if you think you may have done something wrong, go ahead and leave it there for a while until you are sure the mover is working).

    Now, click on the "add movable brush" icon in the toolbar (the cube with the swirls coming out of it).

    caption: This is the mover, placed in the doorway.

    Next, we are going to make a place for the mover to move to above its position. Grab your builder brush, and put it just above the mover, but aligned to the mover in all other dimensions. Choose a texture for this slot, and subtract.

    caption: Here you can see the mover with the builder above it.

    Now, right click on the mover, and select "Mover Keyframe -> Key 1". Now, slide the mover straight up into the slot we made for it. Ok, next we need to put it back on keyframe 0, so select the mover, right click on it, and select "Mover Keyframe -> Key 0". Guess what? You are ready to try this door out. Hit F8, rebuild, and then hit ctrl-p to try the level out. The door should go up in the air when you bump into it. Give it a try.

    PHASE TWO:

    Well, we made a door. We made it move. Is that an advanced door tutorial? I don't think so. Notice that the door we made only moved when we bumped into it. And further, it made no sounds at all. Although personally, I like doors to work when you bump into them (I think it is more realistic), the doors in the single player game don't work like that. And I shouldn't be restricting you to something, should I? :-) Another good point would be that for a door to move up vertically, we probably aren't LIFTING it, so it would make sense for it to move by itself, and if it moves by itself, then we shouldn't HAVE to bump into it.

    Ok, enough rambling. What we are going to do now is gradually adjust our door bit by bit. I will name each new modification to the door a "PHASE" so you can clearly see when we are about to do something new to the door, and have finished a modification. Our first modification is a very very simple one. Lets add some SOUND! :-)

    Right click on your mover, and choose "Mover Properties". Take a look at the section called "Mover Sounds". For right now, we will just mess with the "ClosedSound" and the "OpeningSound". I think once you see these, you will understand the process. You will probably want to do something for "MoveAmbientSound" occasionally. Select "ClosedSound", and click on the three dots to the right of it. You will notice that "SoundFX" is selected on the far right toolbar in the editor. Click on the "Load" button over there, and choose "DoorsAnc". For ClosedSound, lets choose "adend53", then in our properties dialog, click the button marked "use". You will see the sound listed there. Now, do the same thing for "OpeningSound", except choose the sound "adend52". Close your mover properties window, do an F8, rebuild, and ctrl-p the level. Now, when the door moves you should hear sounds. :-)

    caption: Here, you see the filled out "Mover Properties" dialog, complete with an ambient move sound.

    PHASE THREE:

    Didn't that seem more realistic? Sounds add so much to the realism of a level in my opinion. Ok, next, lets make it so you don't have to bump into the door to open it. What we will do is make a trigger that opens the door when we walk near it.

    First thing we will do is name our mover. Why do we need to name it? Well, triggers affect specific objects (these objects all have the same tag), and if the objects don't have a unique tag name, the trigger won't have anything to act upon. So, right click on your mover, and choose "Mover Properties". Next, choose "Events", and you should see a section marked "Tag". Change it to say "door1". Now we have a unique tag name to use for our trigger. But since we are already in our Mover Properties section, lets change one other aspect of our mover. If you remember, bumping into the mover caused it to move. What we want now is for the trigger to cause the mover to move. So we have to change how the mover acts. Open the section called "Object", and click on the section marked "InitialState". You will see it is currently marked "BumpOpenTimed", well, that pretty well describes how it works right now. Choose the drop down arrow next to it and change the value to "TriggerOpenTimed". Ok, now our mover is all setup to be triggered.

    caption: These are the relevant sections of the mover properties with the new values for being triggered.

    Ok, so it should make sense to you that we need a trigger, right? :-) Lets add one. Go to the Classes section on the "Browse" drop down menu. This is on the right hand side of your editor screen if you didn't know. :-) You will notice an item at the bottom called, interestingly enough "Triggers". Well, expand this section, and expand the section inside called "Trigger". Quite a few options eh? We aren't going into them all now. We just need a simple trigger.

    Choose "Trigger", and place it on your map. I have said before that I prefer to add items in the XZ or YZ views (the two views on the right of the editor by default). The reason I prefer to do this is that you place the item at the correct Z height, and only have to move it to the correct XY position, easily done on the overhead map. And on simple maps like this, it normally picks the right XY position for you. :-)

    Put your trigger on the floor, right in front of the door, perhaps 5 feet or so in front of it, in the middle of the doors' width. Ok, now we need to modify the trigger properties. Go ahead and right click on the trigger, choose "Trigger Properties", and then choose "Events". Now, change the "Event" from none to "door1". This will cause the door to be acted upon by the trigger. Just fyi, under "Trigger", you will find an item called "TriggerType", and this has some interesting different ways of triggering the trigger. But the default value is fine for us.

    Ok, do an F8, rebuild, and ctrl-p. When you get into the level, be sure to walk in a straight line for the door. By default, the trigger area is pretty small. Keep moving around until you find it. (If you placed it right in front of the door like I described, it shouldn't take but a second to find it).

    You will notice that if you move through the doorway into the other room that you will not be able to go through the door. You have a "one way door" right now because there is no trigger on the other side. As an exercise, you might want to add another trigger in the other room and make sure you can get through the doorway from either side. Just follow the same directions for the other room.

    You will also notice that there is a red line drawn from the trigger to the door. The red lines on maps allow you to easily see the "cause and effect" of your triggers, ie, what area has the trigger, and what the trigger is effecting. Do not forget this, because when your maps get very complicated, without the lines, it would be very easy to get lost.

    One last item for this phase. If you wanted to, you could increase the area of effect of the trigger by increasing the "CollisionRadius". This is located in "Mover Properties" under "Collision -> CollisionRadius". This will allow you to be in a less specific area. You can even increase the radius fairly high, and place the trigger right under the door, then you can approach the door from either side, and activate it with only one trigger.

    PHASE FOUR:

    Man oh MAN is this a long tutorial or what? :-) And I am not even getting paid for this. What was I thinking? Oh well, hopefully people will benefit from this. Next, lets make a panel on the wall next to the door that causes the door to open. We will be using the trigger to cause this to happen.

    If you increased the CollisionRadius in the previous phase, reduce it back down to something like 150. If not, go ahead and leave it where it is.

    Now, take your builder brush, and create a rectangular panel looking shape, that isn't too deep. Choose some appropriate seeming texture, and place the builder brush to the left or right of the door, at a height that your character can walk up to. Next, move your trigger to be right in front of the panel, and in the middle.

    caption: I made mine as a small button type panel near the floor to the left of the door.

    Now, if you positioned your trigger in the right spot, all you should have to do is F8, rebuild, and ctrl-p to try it out. If you bump into the panel, the door should open. You can probably remember some spots like this from in the single player game.

    Again, unless you put a trigger on the other side of the door, this will be a one way door. Remember, you can simply duplicate this little panel and put it on the other side of the wall with a corresponding trigger to be able to open it from either side.

    PHASE FIVE:

    Yup, still here, still adding stuff to this door. :-) Well, we are gonna get a bit fancy now. Let's make a mover out of our panel. We will make it move when we push it. Add some sound to it, and it will seem that much more real! Ok, surround your panel with a non scaled brush ( refer to the first mover we made if you forgot how to do this), and intersect it. Subtract from the wall a spot large enough for the panel to slip into, then move the brush back to the panel. Then, copy the polygons to the brush, move the brush out of the way, and delete the original panel.

    Now, move the brush to where it is just at the edge of the hole in the wall that you made. Add special, and voila, we have our mover. Move the brush out of the way. Now, right click on the mover, select the second keyframe (keyframe 1), and move the mover into the hole in the wall. Now, right click on the mover again, and put it back at keyframe 0. Add some sound if you want (You might use adend50 for the switch opening sound). Make sure your trigger is right on top of the switch. Do an F8, rebuild, and ctrl-p to play it.

    Now, when you walk up to the switch, the switch should move into the wall (and make a sound if you did that), and simultaneously, the door should open up. Pretty decent eh?

    PHASE SIX:

    Well, I don't know why I keep going on this, but I am. :-) We are going to make one final modification to this whole thing. We will make the door open as two pieces, and have the two pieces open to each side. What we will do first is delete the subtracted region above the door (since it won't be going up anymore), and removing the mover for the door itself. But leave the switch and the trigger there, we will be needing them.

    After you have removed those items, lets build the new door. We will do this by making the same shape for the new door, again, up in the air. Next, we will subtract a small section from the middle of the door. This should be a narrow slice down the center of the door vertically.

    caption: Here, you can see the door, complete with a vertical slice down the center.

    You need to intersect the two halves and place the movers. To do this, select your brush, reset the scaling again (if you forgot how to do any of these steps look at the previous phases in this tutorial), and enclose the right half of the door with the brush. Make certain that only the right half of the door is enclosed, no part of anything else being in the enclosure. Now, intersect the door half.

    Now, move the builer brush to a position in the door frame such that if the right half of the door was to slide fully to the right, it would have someplace to slide into. Make certain it is in the correct position in all dimensions. Once you are certain of this, make certain that if you were to subtract the brush, that it would create a visible hole in the side of the door frame. What I mean by this is that if your brush is too far into the wall, you would subtract it away, and see no visible difference (of course, if that ends up happening, you can just hit undo and try again). Once you think it is all lined up correctly, subtract it away from the doorframe. You should see a spot for your door piece to slide into now.

    Next put your brush so it is exactly on top of the right half of your door again. (This should be where the brush was when you intersected it before). Now, right click on the brush and do the "Copy polygons -> to brush". You are ready to place the mover for the right half of the door. Put the builder brush just to the left of the hole you cut out for the door half. In this position, the door should cover half of the doorway. Make certain it is aligned in every dimension, and when you are certain of this, click on the "add mover" icon (the block with the swirls coming out of it).

    You have done half of the door. Repeat the above procedure exactly for the left half of the door. When you have finished the process, delete the door and the subtraction down the middle of the door from the middle of the room (this is the door that you intersected that I am asking you to delete).

    caption: Here, you can see the two movers in place, ready to be told how to move.

    We are nearly done! Select the right hand door mover, then hold the control key down and select the left hand door mover. You should now have them both selected. Next, right click on one of them, choose "Mover Properties", and we will modify a few settings. These will be the same as before. Under "events", change the "Tag" to be "door1", and under "Object" and change the "InitialState" to be "TriggerOpenTimed". You should now see a red line on your 2d views that goes from your trigger to your door mover pieces. If you don't, something went wrong, go through the procedure and try to find what it is.

    Next, and this is VERY critical, unselect your movers, and select ONLY the right hand mover. You do not want them both selected for this part. Right click on the mover, and select "Mover Keyframe -> Key 1". Now, move the mover so that it is in the recessed portion of the wall, in the hole that we made for it. If you want, you could leave a little bit of it still sticking out into the doorframe area. When it is in the position you wanted, right click on it again, and select "Mover Keyframe -> Key 0". Next, do the same thing for the left piece of the door, putting it into the hole on the left.

    If you want, you can add sounds to the door pieces like we did before. As I mentioned earlier, I think the sounds add a lot to the feel of a level.

    Guess what? You are ready to try this thing out! F8, rebuild, and ctrl-p the level to see how it works. You should be able to hit the switch on the wall and see the two pieces of the door move into their respective chambers, making room for you to walk through the doorway. As before, if you want to open the door from the other side, you will need a trigger on that side for it to happen, but I think you are advanced enough now to know how to do that! :-)

    Take this tutorial, go through it a couple of times until it seems clear to you what the steps are. Then, design some doors on your own that make use of the principles I have shown you.

    One last thing. I will be adding to this tutorial later on to give you a door that has moving gear pieces in a VERY elaborate technique such that you can see the actual mechanism that opens the door. This tutorial was long enough and SOOOOO many people were emailing me that they wanted it, that I have decided to release it now. I will add to the tutorial later on.

    Good luck, and may all of your levels be awesome! :-)

    -Joe Chott (AKA Verxion)


Custom Sound

Custom Sounds in Unreal

    Nemesis
    nemesis@teamanarchy.com

    What you will need:

    1. A sound editing package. I recommend Syntrillium's CoolEdit 96* (or CoolEdit Pro if you feel like shelling out the bucks.

    2. A "tracker" program*. Trackers allow you to compose music (modules or "mods") by arranging digital samples in the order and pitch you want them (much like laying out notes on a staff). I recommend Fasttracker 2.08*. Most trackers are DOS based (it's a tracker thing) and usually support SoundBlaster, GUS, or SB emulation. An .XM file has been included with this package for you to try the exercise below.

    3. A good ear for music and a bit of patience. Learning to track songs takes time.

    What you will NOT learn:>

    This tutorial will NOT teach you how to compose music or how to work a tracker. You'll have to read the docs with the tracker or find an online FAQ*. There are also a good variety of tracking tutorials available online*. Beyond that, most music is taste so use your best judgement on what samples* to use and what kind of song you want.

    This tutorial will also NOT teach you the art of Foley (making sound effects). Foley is based on pure ingenuity and is up to you to come up with (or find) the sound effects you want. Here's a freebie (and an example of Foley): Smacking a head of lettuce with a broom handle sounds suprisingly a lot like a skull being cracked in...

    On to the goods...

    Part I: Music

    Start by making a room to play around in (add a playerstart and lights). First thing you'll need to do is create an unreal .umx music file. You can start by switching your browser (on the right) from Textures to Music. Click the Import button at the bottom and select your music file (created in FastTracker or something similar). Unreal supports .mod (standard mod file), .s3m/.stm (ScreamTracker format), .it (Impulse Tracker), .xm (extended mod format), and .far files.

    Your music file will appear in the list to the right. From here you can play it if you wish to make sure it sounds correct. Just click Save and give your music file a name (such as "Trance.umx"--whatever you want). Now that your file is saved you can go to the Options menu, select Level Properties, go down to the Audio section, choose Song and click 'Use' (make sure the song is selected in the list to the right). That will cause your song to be played for the duration of the level.

    There are other ways to start songs (such as triggers) if you prefer to have a song keyed to and event (you have the option of playing it through only once using triggers). Try out the above with the included .XM file.

    Part II: Sound without compromise

    Sometimes finding the right sound for the job is easier said than done. After listening to every sound effect in Unreal and still not having what you want, you tend to get frustrated. Finally you decide, "Screw it, i'll find a .wav or make my own." Well, if you've ever used Windows recorder than you know it's sadly underpowered and churns out huge .wav files. Similarly, you may find the sound you want on the web but it's about 5 times larger than it should be (you don't want your level to be 3+ MB now do you?).

    The thing to do is either A) record and edit your sound using a righteous sound editing tool (such as CoolEdit*) or B) get a pre-made sound into that same righteous sound editing tool. You'd be suprised how much quailty is retained by a sound file if it's recorded at 44K/16bit/Stereo and then downsampled to 11 or 22K/16bit/mono. Use your sound editing package to do this downsampling. Listen for audio quality. You WILL find a nice compromise between quality and size.

    Once you've got your sound effects made and edited, fire up UnrealEd and switch the browser from Textures to SoundFX. Since you can't just create a .uax audio file directly you can do one of two things: 1) Load up Extro.uax and delete the 7 sounds in there, Import your sounds, and save it under a different name (BE CAREFUL not to overwrite Extro.uax!) or 2) Just import your sounds into Extro.uax and leave them packaged in there with the first 7.

    Now you've got exactly what you wanted in a sound, and it's small so it doesn't bloat your level up. You can check out the "Advanced Triggers" tutorial on unrealed.net for details on using triggers to fire off sound effects and music. If you have more questions about sound editing and music tracking you can contact me at nemesis@teamanarchy.com. Please do not bother the webmasters with sound editing/tracking related questions. Happy editing, and happy tracking!

    *Some helpful links to references made above

    Syntrillium Software (creators of CoolEdit) (http://www.syntrillium.com)
    A good collection of Trackers (http://www.maz-sound.com/trackers.html)
    The Zen of Tracking (online resource) (http://falcon.invincible.com/~jerm/newcomers.html)
    Some samples to get you started (http://www.th-zwickau.de/~maz/instruments.html)


Elevators

Basic Elevators

    Verxion
    jc83151@goodnet.com

    Welp, this is my very first tutorial for UnrealED. I sincerely hope people will like it. If you find any problems with it, PLEASE email me at jc83151@goodnet.com, and I will make every effort to improve the tutorial. Before I go any further, let me explain that I am very new to Unreal, and to the "editing" scene as well. I am currently a programmer/security analyst at Boeing. But I used to do desktop publishing for a living, and I have found that a lot of what I learned there is applying to UnrealED. I am also hopeful that the fact that I am new to editing levels in general will make my tutorials more useful for people new to this program.

    ABSTRACT:

    This first tutorial is going to go over simple elevators. Let us consider what the pieces are that we need to get elevators to work, and what some of the terms are that we will be using. The elevator itself will be a "mover". A mover is simply a "brush" that is purple in color, and is capable of moving. The mover will move through a series of positions called "keyframes".

    ASSUMPTIONS:

    You know how to make a room, put a playerstart in it, how to move and shape your brush, and deal with textures.

    TUTORIAL:

    First off, make a room at least 256x256x256 (although this will be somewhat cramped). Next, reset your brush, and make a cube that is squashed such that it would be short enough to walk onto. Make sure there is enough height and width that you can stand on it. Put this brush in mid air. Choose a texture, and "add" it into the room.

    caption: here, you see the textured elevator floor in mid-air

    Now, right click on your brush. Choose "reset -> reset scaling" This step was very important. Let me explain why. If you did not reset the scaling at this point, the elevator would not work properly, instead, it would allow you to walk THROUGH it instead of on top of it. So if, after making your elevator, you find that you can walk through it and it doesn't work right, this was probably the step you messed up on. :-)

    Next, move your brush so that it surrounds your elevator floor piece. Once you are certain the brush fully surrounds it intersect the elevator floor. (To do this, you can either click on the intersect button in the toolbar, or go to the "brush" menu, and choose "intersect). Now, right click on the brush, and choose "Copy polygons -> To brush". This will copy the textures and the shape of your elevator floor into memory.

    The step I am about to give you can happen anytime that you choose. If you are confident that you did the previous steps right, you can do this step now. If you think something may have gone wrong or are unsure, you can wait to do this step until after you try your elevator. This step involves the elevator floor you just copied. It is hanging in the middle of your room. You need to delete this at some point. :-) You can either do it now, or after you know your elevator works.

    Next, place your brush onto the floor where you want the elevator to be. (This is the spot the person will walk onto in order to activate the elevator.) Once you have the brush positioned where you want it, click on the "add mover" icon in the toolbar. (The box with swirling mist coming out of it.) Move your original brush out of the way, and you should see the purple "brush" that is called the "mover".

    caption: Here, in purple, you can see the mover at keyframe zero.

    Next, we need to tell UnrealED where the elevator is going to go. Right click on the purple mover, and choose "Mover keyframe -> Key 1". Now, in the perspective view, while the mover is selected, hold both mouse buttons down and translate the mover vertically. I recommend doing this in the perspective view because you can see clearly where you are placing the elevator, and you can easily restrict the motion of the mover to the Z axis only.

    caption: Here, in purple, you can see the mover at keyframe one.

    Almost done. Just right click on the mover one last time, and put the keyframe back to zero (mover keyframe -> Key 0). This sets the initial position of the mover for when the map starts. Now, you can F8, rebuild map, and ctrl-p it to try your new elevator out!

    Good luck. :-)


Fire

    BEG

    ASSUMPTIONS:

    This tutorial assumes that you know your way around the basic interface of Unrealed.

    TUTORIAL:

    Ok after you have UnrealEd loaded you can load up the texture sets that you will need, over in the Browser pane you should see a group of buttons at the bottom of the screen, press Load and load up the Ancient and GreatFire packs.

    Now we want to make a simple room, right click on the cube brush and select "Cube Properties..." this will pop up a box where you enter the width, height etc. Change the Height, Width, and Breadth values to 512, hit build this will resize the cube to the new values, then hit close.

    Ok now goto your Browser pane and select Ancient from the dropdown menu and select a wall texture you want to use, hit subtract and we now have our room. You can now change the floor and ceiling textures if you want.

    Now right click on the Cylinder brush and goto "Cylinder Properties..." Change the height to something like 128 and both Inner/Outer Radius to 64, hit build. And close the popup window.

    Move the Cylinder so it is in the center of your level and is flat on the floor, select a new texture for the Cylinder if you want and Add it to the world.

    Right click on the Sheet button on your toolbar menu and select "Sheet Properties..." This is what the fire will be placed on. Set the properties to Y-wall, U value of 64 and V to 128. Build the sheet and close the popup window.

    Move your sheet so it is in the center and sitting on top of your Cylinder that you added into the world. Select a fire effect to apply onto the sheet, such as 'ANCPURP'.

    Click on Add Special brush in your toolbar, this will popup a new window, on the drop down menu select masked texture, then change the visibility options to 2-sided, and make sure Transparent is checked in Effects. Click add special and then close. The fire texture may be tiled but dont worry about it at this point.

    Click on the rotate button on the toolbar and rotate your red sheet builder so it is at a 90 degree angle from the one you just created, and repeate step 8.

    Now your fire texture is probably tiled so highlight them in your perspective window and enter their surface properties, under scale make Simple Scaling 2 and hit apply, the textures should be sized correctly now.

    Place lights, now place a light or mutiple lights and tweak them until you are satasified with the lighthing.

    Note: Don't place lights directly above your fire effect they will drown out the texture and will make the fire like ugly.

    Add a PlayerStart and rebuild your level.


Fog

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    Well, I just added some fog into a level that I am working on, so I had best make a tutorial that delves into fog, and how it is used in UnrealED.

    Abstract:

    I will explain the basics of fog and how to use it, as well as explain colored fog.

    Assumptions:

    I assume that you know how to create lights, as well as a basic room (at least).

    Tutorial:

    Introduction:

    Fog adds a feeling of being there, and that's what we wnat from our levels ins't it?

    Setting the scene:

    Before we get into fog, we must allow our editor to draw the fog. When you have selected a room for the fog to go, and go into the texture bar, but select "classes" now go into Classes>Info>Zoneinfo . Now add a "Zoneinfo" into your level. It doesn't matter where you place it, as long as it is in the same zone (don't place it underwater, if you want the fog just above the water level for example) as where you want the fog to go. Now open up the advanced properties, and then the zoneinfo tab. Change the "bFogZone" to TRUE, and close the advanced properties.

    The light settings:

    Well the hard part is out of the way, so now it's a simple amtter of placing the lights where you want them, and editing their settings like this...
    The "Volume Brightness" setting will determine how bright the light is inside the volume of it's area. The "Volume Fog" setting will determine how "thick" the fog is. The "Volume Radius" setting will determine how large the area is that the light and fog effect. It is possible to play with color settings as well, to create colored fog. In the example show here, I have created purple fog (No not a "Purple Haze" :)...).

    A Little Warning:

    Fog !!SLOWS!! down pc's, and it should really be used sparingly. Creating a Huge outdoor setting, then filling it with fog might sound cool, but unfortunatley, it would be unplayable on everything but NASA!
    If you have multiple zones, you will find that fog cannot be seen from one zone to another, so if you want multiple zones, then make sure you place a "Zoneinfo" in each zone where you want the fog, and set it to the above configuration.


Light Properties

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    I have just finishes the Light Color Tutorial, but there are so many more things that lights can do, so let's get into it.

    Abstract:

    Lights can do MANY wonderful things in Unreal, and to not know how to use them is a crime against yourself, as they add SOOOOOOO much depth to a level.

    Assumptions:

    I am assuming that you know how to create lights, change colors maybe and are familiar with the UnrealED interface.

    Tutorial:

    Introduction:

    There are a million things that lights will do for you in UnrealED, but I will go through a few of the better effects, and hopefully by the time you finish this tutorial, you will be able to get more out of lights, and the other effects that I have not listed here will be a simple click away...

    Watery Shimmer:

    When you have pool of water, adding this type of light will make it HEAPS more realistic. It makes the light "refect" off the water ans fill the room with a shimmering light. The picture will explain the settings of the light.

    Colored Lights:

    Colored Lighting will ALWAYS make a level more realistic, and I have included this simple light here as well. I have posted a full tutorial on colored lighting, and if you want more information about changing the color of the lights, please see that tutorial.

    Disco Lights:

    This type of lighting effect will work !!GREAT!! for a power generator room, or a "high pressure" type of environment. It will leave little gaps for people to hide in, whilst no making them invisible for others. This would be ideal for an "emergency" situation as well.

    Flickering Lights:

    Flickering lights add a great level of realism to a part of a level that is experienceing "power problems". It will also function well for a underwater light, as the water may have seeped through the insulation and may be causeing the bulb to "short out". Once again, use is sparingly to add "eyecandy" for that special part of a level.

    Search Lights:

    A search light will add great realism to a "get out of the prison" level, or make an "emergency" feel much more to the point.

    Strobes:

    A strobe will make play more difficult, as things will "jerk" from place to place as they are lit and hidden in shadow, but if used here and there, this will add a great "room" to your level.

    Sound:

    Yes, that's right, you can edit the sound property of your light. For example, in a "red alert" setting, you may want to have an alarm blaring. To do this, select sounds, then load a sound package (to do this, press the "three little dots" in the ambient sound box, and you will be show a sounds bar. Select load from the options at the bottom and load a sound package (like ambmodern) and choose the sound that you want played with the light.) When you have found a suitable sound for the occasion, select "use" in the light properties box (next to the three little dots) and rebuild the level.


Colored Lights

    Verxion
    jc83151@goodnet.com

    FOREWORD:

    Installment number 2. I decided to do colored lighting next since it is so gosh darned easy! :-)

    ABSTRACT:

    This tutorial will go over colored lighting, and how it is done within Unreal. Colored lighting is really quite simple to do. Just remember, don't go nuts with colored lighting or you can overwhelm people. :-)

    ASSUMPTIONS:

    You know how to make a room, put a playerstart in it, how to move and shape your brush, and deal with textures. Additionally, you should know how to place a light. If you don't know how to place a light, try rightclicking on a view window. :-)

    TUTORIAL:

    The first thing you need to do is make a test room. Make this one fairly large so you can have lots of different lighting in it. I leave it to the student to know what "fairly large" means. :-)

    Well, I have been thinking of the best way to show you guys how to make colored light. Since all I am really covering is how to make a light have color, this assumes you already know how to create a light in the first place. What I will do is show you how to make a room with some cool colored light, that has some cool lighting effects that are darned simple to do.

    Since you made a room, you must know how to size your brush. Well, size your brush so that it is fairly small. How small? Well, how about 25x25x25 or so. Hopefully you can fit several of these into your room. Arrange three of these cubes on the floor as shown below:

    caption: here, you can see 3 cubes on the floor, waiting for colored light to "happen". :-)

    caption: These are the same 3 cubes on the overhead (xy) view.

    See the note at the bottom of this tutorial now. It is very very important to helping you understand the rest of the tutorial, but it is kindof off of the real topic at hand. If you already know what Hue and Saturation are, you probably will only want to glance at the important note after going through this tutorial.

    ***Go read the important note, then come back to this part of the tutorial***

    Now, place a light in between the three cubes, at a height halfway up the cubes. Right click on the light, and choose "properties". Now, under the properties, choose "LightColor".

    caption: This shows the LightColor section of Light Properties.

    Now, hopefully, you know what Hue and Saturation you want. Place the values you want in those two spots. If you don't see the color you expect, lower your saturation value, and it will VERY VERY likely fix your problem. If you don't have a specific color in mind, we can choose values of 200 for brightness, 30 for the hue, and 64 for the saturation. Now, make certain that this one light is the ONLY light on your level, and do an F8, rebuild, then ctrl-p and take a look at it.

    Now, change the value of the hue to each of the following values: 230, 163, and 100. You can see quite clearly that the Hue value walks you around the color wheel.

    caption: Sample of how the colored lighting can look.

    Well, you are now off on the great road of color! Use the force wisely, for it can corrupt the soul, er, um, nevermind. :-)

    Good Luck!

    IMPORTANT NOTE!!!:

    Ok, at this point, we are going to make a diversion from an otherwise perfectly normal tutorial. :-) One of the reasons colored lighting can be so difficult for people is that 99% of it is done "blind". You just type in numbers and try the level. This is VERY tedious. It turns out that inside of UnrealED there is a place that has the colors, and they are labeled. But this area isn't available while you are editing light colors. A poor design decision. What I am going to do here is show you how to get to this color area. Once I have done that, we will resume the tutorial. If you don't already have a "zoneinfo" on your level, make one (it is in classes -> Actor -> Info -> ZoneInfo). Inside of ZoneInfo, choose "ZoneLight", and click on the black color bar next to "FogColor". Now, to the right of the black color bar, you will see three dots in a button. Click on this button.

    caption: This is the ZoneInfo Properties area that leads to the color chart.

    caption: This is the color chart itself.

    Ok, now you should see all kinds of colors. To my current knowledge, this is the ONLY place that allows you to see colors in a chart like this in UnrealED. Important things to note when looking at this chart is the block of color, the slider to its' right, and the text entry boxes under the color block. The important text entry boxes are "Hue", "Sat", and "Lum".

    You can click in various different spots in the color block, and the values in the entry areas will change to reflect the color you have chosen. To the right of the color block, is a small slider that lets you adjust the "brightness" of the color, and this value is reflected in the entry marked "Lum". The color you are creating by using these tools is shown in the "Color|Solid" area.

    PLEASE understand that this color chart is intended to be used to adjust that fog color I showed you earlier. This tutorial is about colored lighting, and we won't be dealing with fog right now. Therefore, I am just showing you this area since it will allow you to visually see what "Hue" and "Sat" you want for your colors.

    Ok, return to the previously interrupted tutorial. :-)


Colored Lighting

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    I wanted to show everyone how EASY it is to have wonderful colored lighting in a level, and the best way t do that is to post a tutorial.

    Abstract:

    Colored Lights are simple, but you have to know how to do them first, so here goes.

    Assumptions:

    The only assumption I make here is that you can follow instructions... hehehe :)

    Tutorial:

    Introduction:

    Okay you have wonderful lights all through your level, but to add that little extra, why not make them all different colors as needed?

    Placing the light:

    I won't go into detail here, as you should already know how to place a light, but if you have somehow missed it, then all you have to do it right click where you want it (in the 3D veiw) and select "add light here" from the drop down menu.

    Setting the Color:

    Once you have added the light where you want it to go, all we have to do is set the color. Once the light has been placed, select it and bring up the advanced properties. Then bring up the "lightColor" tab. Here you will find three setting. The first will determine how much light the light actually produces. The second is what color the light will actually make. The third sets how much saturation is in the color (how affected the area around it is...)

    Option One - The Lightbrightness:

    This one is pretty straight foward. The more brightness your light has, The brighter it will show up and brighten the area around it.

    Option Two - The Light Hue:

    Light hue means the color of light generated by the light. This is where the actual color setting is selected. It is as easy as entering a number for the color you want. Unfortunatley, when making a map in the editor, there is no way to find out what color is selected by a particual number. (Well not really anyway). I have added a few colors and their numbers, to make life easier.
    Red = 0
    Yellow = 23
    Green = 64
    Blue = 128
    Purple = 192
    Red = 255
    The numbers are actually representing a color wheel (remember those things you made in primary school?), so as you get close to the end (255), you will notice the colors start to go redish - until you get to 255 where the next color is zero again.

    Option Three - LightSaturation:

    Light Saturation basically means how the textures around it will be effected by the light color. The defualt setting is 255 - Which means the textures around it will reflect ALL the possible colors generated by the light, but to have nice colored lighting, a setting about halfway down the bar (127 - if you want a number) will allow you to generate any color that you like.

    Rebuild!!!:

    When changing any light setting make SURE you do a complete rebuild (include the "auto BSP" tab with the "Auto Lighting" tab as well) otherwise you will run into problems.


Lens Flares

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    There seems to be a great big huge whopping demand for this tutorial, so here it comes.

    Abstract:

    This tutorial will show you how to add in Lens Flares into your lights. It will also show you how to change them so that they look the best for your level.

    Assumptions:

    I assume that you already know how to make lights, and how to place them in your level. If not, you should have a look at the lights tutorial I have written.

    Tutorial:

    Introduction:

    When you have placed you lights in the level, you will need to open up a new texture package for the lens flares them selves. To do this make sure you are browsing the "textures" in the right hand bar, and click on "Load". When the dialog box appears, open the GenFX.utx package, and you should see all the possible textures that are there.

    Selecting the Flare:

    When you find the light that you want to have a flare on, bring up it's "advanced properties" by right clicking on it, and then go to the Textures bar. Choose a texture that will suit your room, and click on it. Then go back to the Light Properties, Choose DISPLAY>SKIN and you should see a little Input there. Press the "use" button and your new texture's name should appear in the box.

    When that is done, go to LIGHTING>BCORONA and set it to "true" (defualt is "False")

    Adjusting the Size of the Corona:

    You can at this piont rebuild your level (AutoBSP and Lighting) but your flare would probably be way too big to look good. Let's fix it to be the right size eh?

    Select the Light Properties again, and choose DISPLAY>DRAWSCALE. The defualt here is 1.00000 but you will probably want to have your one set between 0.3000 and 0.5000 (Hell, I am guess how big you want it here. :)

    Lastly:

    There is just one more thing I think I have omitted so far... In the Light Properties, you have to change a few settings. Select LIGHTING>BCORONA and set it to true, otherwise your Flare won't be visible, and there is another setting there called bLensFlare, but that one doesn't seem to do all that much, so you might just leave it set to "False".


Liquids

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    Okay, Here comes an easy tutorial on how to make water...

    Abstract:

    Making water is VERY easy, but you have to know how to do it first...

    Assumptions:

    I assume that you know how to make basic rooms.

    Tutorial:

    Introduction:

    Water adds a lot more depth to a level, whether it be in the form of a pool, a puddle here or there or perhaps a whole "Flooded In" level, with air pockets here and there... I assume that you have a level where you want to place water, so you should start off with a room allready...

    Creating the water "hole":

    Just as you would normally create a room, or a hallway or whatever for that matter, "cut" out the space for the water to go. I mean if you want a pool in a certain room, then make the room and cut out the place where you want the water to go into. If you are just starting out, it's a good idea to stick with simple things first, maybe a small square pool.

    Adding the water sheet:

    Now that you have a place for the water to go, select the sheet* brush. When you see a thin red line, simply select the sheet properties (right click the sheet button) and select Floor ceiling orientation. The size of your brush should be a little larger then the hole you created for the water level (ie. It should actually overlap the sides). Now place it just below the level of the sides - otherwise it looks like it's going to overflow) and hit the "intersect" button (the fourth one down on right hand column). This will then make the brush fit your water level EXACTLY. At this point you might say why not just make the same dimensions, right? Well that is becuase if later down the track, you want to add in things like rocks jutting out of the water, or you have water levels that are not rectangular, you will allready know how to make the proper brushes for them - rather then having to learn a whole new method. Better to get it right the first time. You will have to load in the textures for water, so under the texture bar, select "load" and scroll along until you find "liquids", load it and choose a water texture for the sheet you want to build. Now that you have the sheet the right size, and in the right place and with the right texture, click "add special" (the fifth button down right hand column) and from the drop down list, select water. Simple isn't it??? At this point, sometimes - the water sheet won't show up - if this is the case, just "rebuild" the level, and it will be there.

    Creating the water itself:

    Now that you have the water sheet, you can add the zone info for water... Where the texture bar is, select "classes" from the drop down list (it should currently display "textures". Choose info>zoneinfo>waterzone and right place it into the area your pool is in (right click in either a 2D or 3D view) and make SURE it is inside the area you want the water to occupy.

    Adding sound:

    When you select the waterzone info and place it into the map, it will automatically add some sound into the water for you, but you can change the actual sounds that you hear by bringing up the waterzone properties (right click the waterzone icon - the cube with a question mark next to it). Then select "sound" and choose "ambientsound" this will then bring up some buttons. Click the 3 dots and you will be shown a list of sounds to choose from. If you haven't loaded any sounds, they won't be there yet, so to load them click the "load" button below the "sounds" bar and choose "AmbOutside". Then in the drop down list select "water" and play them until you find one that will suit your needs. Go back to the properties and under AmbientSound, choose "use". That's it, Done.

    Rebuild and Play:

    Simply rebuild your level, and play it to check out your water settings. To see more cool things that can be done with water, look out for the "Advanced Water tutorial that I will be posting shortly.


Movers

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    Since doors and all the other stuff that moves are really the same thing, I will post them as one tutorial.

    Abstract:

    I have been asked to post a tutorial about doors, I have also been asked about other movers. They really are the same thing, so I will be posting them as one tutorial.

    Assumptions:

    I am assuming that you know how to use UnrealED with a fair amount of practise. I don't expect you to be perfect - otherwise you wouldn't be reading this tutorial :)

    Tutorial:

    Introduction:

    I will be starting with simple mover (A door I think) and going on to more and more complex things. I would expect that you are quite famailiar with UnrealED to complete this tutorial entirely, but if you just want to know how to make a door move; then this will also help you out. Feel free to stop when I have reached the level that you want to go to...

    The starting point:

    You can do this in a level that you have allready created, but I would ask that you spend the time and just start a new one for this tutorial. This way, you will be able to follow my instructions easier - and you will repeat the doors when you go back to your own level, further imprinting it in your mind.

    The room:

    Let's make a room that is fairly large. Create a cube 256x1024x1024 and make it look like a room (Textures, Playerstart and lights ect). When the room is complete, make a cube 256x1024x32 and place it in the middle of the room to make two rectangular rooms. This will form the basis of the tutorial. Now in the middle of the wall (At floor level) create a cube 128x128x32 and "subtract" it from the wall. This will be the doorway.

    A simple swinging door:

    To make the door move, we have to create a mover; so here are the steps. Firstly create the door with a cube (128x128x16). Make sure however when you are creating the door, that you create it in the MIDDLE of one of the rooms ans in the air (as opposed to in the doorway). When you have made the door, make sure that you are happy with the textures, the texture alignment, scale and the like as there will be no way to change them later.

    When you are happy with the way the door looks, make a brush that is bigger then the door (150x150x100 for example) and place it around the door (make sure that the new brush completely covers the door) and press the "intersect" button.

    If you are not sure at this time, check out my buttons tutorial and come back. When you have intersected the door, move your new brush to the doorway, and place it where you want it to go. Now click the "add mover" button. Move the red brush away from the door (it will only get in the way) and select the mover (Left Click). Once it is highlighted, right click on it (making sure it is the only thing selected) and select "Mover Keyframe --> Key 1". Now rotate the door to an open angle and make sure the hinges are still close to the wall, right click and select "Mover Keyframe --> Key 0" At this point your door should move back to the doorway. You should be ready to try it out now. Rebuild the level and have a go...

    Sliding door:

    You will at some time want to have a sliding door, so let's get into it... To build a sliding door is actually easier in a way, then a swinging one. All you have to do is create the door as detailed above (ie NOT in the doorway) and when you are happy with it, intersect it with a larger cube. When you have your new brush, move the brush to where you want the door to slide into (Not in the doorway, but actually in the wall (at this point you probably should lower the grid size. If you haven't done this allready; simply Right Click in one of the 2D views and select "gridsize" and then maybe one or two). Now make sure that the new brush "touches" the wall that the door "attached" to. When placed, subtract the brush. You should now have a hole in to wall just to the side of the doorway. You can change the textures if you like, but if you do it properly, then these textures will never be seen anyway :) Now move the brush to where you want the door (This should be just to the side of where you cut the hole just earlier) and click the "add mover" button. Get the red brush out of the way, then select the mover itself (It has a purple outline) and right click it. Select mover keyframe--> key 1. Now move the door into the hole in the wall and right click it again. This time select mover keyframe--> key 0. Your door should move itself back to where you originally placed it. That is all there is to it. Rebuild the level and try it.

    Automatic doors:

    You should notice that the door work quite well, but you actually have to run and "bump" into them to open them. It would be much better if they just opened when you ran "near" them wouldn't it? The first thing you will have to do is add a trigger. To do this, go to the "classes" menu (it's right next to "browse" on the right of the screen) and select "triggers" from the bottom of the list. Expand the section and select "trigger". Place the trigger in the map (Do this by right clicking in the 3D view and select "add trigger here". When you have the trigger where you want it (it should probably be just in front of the door), and select it.

    Now right click and select advanced properties. In the advanced properties, choose "events" and change "event" from none to "firstdoor". When you have done that, click the door again (The mover actually) and right click it. Select "advanced properties" and go to "events". Change the "tag" from none to "firstdoor". Now this is important, go to the "object" section and you should see a entry named "initial state" this is set to "bumpopentimed" by default. Change it to "triggeropentimed". This is important because even if you have a trigger made for the door (or any mover for that matter) it won't be effected by the trigger until you let it be "triggered" (sorry for the pun hehehe :). At this point, you could rebuild and try it out, but before you do that I will make you do one more thing. Left Click on the trigger (to select it) and press CTRL W. This will create another trigger exactly the same as the one that is there (you can duplicate almost anything using this) and place it on the other side of the door. If you didn't do this, you would have a door that only opens from one side - which is fine if you want to "trap" a player in a particular area, but we don't want to do that in the tutorial... Rebuild and have a go.

    Doors with sound:

    All this should be pretty cool huh? But our doors need to make all those cool sounds that doors make, right? (If you disagree - then the end of the world will occur and all my work will be in vain. OOPS sorry, I forgot where I was :) Where was I, Yes doors that make sounds. Select the door and bring up the advanced properties. Bring up the "mover sounds" and select "ClosedSound". You should see three little dots in the same cell. Click them and this will bring up the "sounds" menu on the right hand side of the UnrealED window. Click the "load" button below the main sounds toolbar. If you like a ancient or fantasy setting choose "DoorsAnc" or if you like a more futuristic feeling, choose "DoorsMod". You can play a sound before you set it to the mover by clicking the sound in the "sounds toolbar" and pressing "play" at the bottom of the toolbar. When you have found a sound that suits your needs, press the "Use" button which is located next to the three little dots (in the advanced Properties window) and this sound will be set to the door when it closes. Please Repeat the sound for the "opening Sound" and we should be able to have a go and play the level. Rebuild it and let's have some fun.

    Additional Notes:

    By this time, you should have noticed that the doors we made are simply made from "movers" not a special piece called "moving door" or something like that. Doors are in no way different to elevators or anything else that moves. By now you should be able t replace the stairs in your level with movers and make the level much more animated. If you would like to know something n particular about doors or how to do something with movers, then contact me at the above address. I think that doors are quite important in a level, so I will go into more (yes that's right, MORE!) depth with doors in a later tutorial, but I think that this will keep most people happy for the timebeing...


Movers 2

    Verxion
    jc83151@goodnet.com

    Third installment coming up. I feel like I am getting pretty good at making these tutorials now. One small clarification I thought I would make is that on IRC, I go by the nick Verxion. So if you see me on UnrealED, take a sec to say hi. :-)

    ABSTRACT:

    Well, I have seen several tutorials around concerning movers, but I was asked to do one on movers, so I shall. :-) Rather than take the "easy" was out and simply do one step of motion with our mover, I am going to go over some of the more intricate things movers can do. I will start simple though, rather than overwhelm you with one of the more complicated ones to begin with.

    ASSUMPTIONS:

    You know how to make a room, put a playerstart in it, how to move and shape your brush, and deal with textures.

    TUTORIAL:

    First off, make a room at least 512x512x512. Next, right click on your brush, and choose "Reset -> Reset all positioning" (Your brush should be 256x256x256 now. If not, please make it so :-). Put this brush in mid air. Choose a texture, and "add" it into the room.

    caption: here, you see the textured block in mid-air

    Now, right click on your brush. Choose "reset -> reset scaling" This step was very important. Let me explain why. If you did not reset the scaling at this point, and your brush had been scaled before hand, it would not allow your mover to work right. Instead, it would allow you to walk THROUGH it instead of being solid. So if, after making your mover, you find that you can walk through it and it doesn't work right, this was probably the step you messed up on. :-)

    Right click on the "Build a Cube" button on the control panel (the cube to the right of the control panel). Choose "Cube Properties". In here, change the height width and breadth to values of 300. This should cause your brush to be slightly larger than the cube you made.

    Next, move your brush so that it surrounds the block. Once you are certain the brush fully surrounds it intersect the block. (To do this, you can either click on the intersect button in the toolbar, or go to the "brush" menu, and choose "intersect). Now, right click on the brush, and choose "Copy polygons -> To brush". This will copy the textures and the shape of your block (soon to be a mover) into memory.

    The step I am about to give you can happen anytime that you choose. If you are confident that you did the previous steps right, you can do this step now. If you think something may have gone wrong or are unsure, you can wait to do this step until after you try your mover. This step involves the block you just copied. It is hanging in the middle of your room. You need to delete this at some point. :-) You can either do it now, or after you know your mover works.

    Next, place your brush onto the floor. Once you have the brush positioned where you want it, click on the "add mover" icon in the toolbar. (The box with swirling mist coming out of it.) Move your original brush out of the way, and you should see the purple "brush" that is called the "mover".

    caption: Here, in purple, you can see the mover at keyframe zero.

    Next, we need to tell UnrealED where the mover wants to move. Right click on the purple mover, and choose "Mover keyframe -> Key 1". Now, in the perspective view, while the mover is selected, hold both mouse buttons down and translate the mover vertically. I recommend doing this in the perspective view because you can see clearly where you are placing the mover, and you can easily restrict the motion of the mover to the Z axis only.

    caption: Here, in purple, you can see the mover at keyframe one.

    We are almost ready to try our mover out. Just right click on the mover again, and put the keyframe back to zero (mover keyframe -> Key 0). This sets the initial position of the mover for when the map starts. Now, you can F8, rebuild map, and ctrl-p it to try your new mover out!

    If you walk up to your mover, you should automatically trigger it to move. Watch it as it moves up into the air. :-)

    Next, lets assume you want some sound effects associated with your mover. Choose "SoundFX" on the browse side, and load "activates.utx". Now, right click on your mover, and choose "Mover Properties". In this dialog, choose "MoverSounds".

    caption: Here you can see the mover properties dialog with the sounds already selected.

    Now, in the dialog, click on the "OpeningSound". On the Browse menu, choose "SoundFX", and "Activates" in the dropdown below it. As an example sound, choose "sclick2". Now, you should be able to click on "use" next to "OpeningSound". If you don't see the "use" button, click on "OpeningSound" in the Mover Properties dialog, then you should see the "use" button.

    You can do the same thing to set "ClosedSound" to "sclick3" and "MoveAmbientSound" to "phumb6bL". Now do your F8, rebuild, and ctrl-p. You should hear sounds now when the cube moves.

    Ok, the last thing I am going to cover in this mover tutorial is the fact that you can have more than 2 keyframes. All of the tutorials on movers that I have seen have only 2 keyframes. Is it hard to do more than 2 keyframes? No. Could you probably figure it out on your own? I think so. But just to be thorough, here we go.

    Right click on your mover, and choose "Mover Keyframe -> Key 2". You shouldn't notice any perceptible change. Slide your mover over on the floor to the right of its current location. Next, you need to change the default number of keys from 2 to 3. You do this in "Mover Properties". Right click on your mover to open the properties. Choose "Mover" and then choose "NumKeys" and type in a value of "3". I think it is the fact that you have to change the "NumKeys" value that keeps most people from doing more than 2 keyframes. Ok, do an F8, rebuild, and ctrl-p to try your mover out!

    caption: Here is the mover at keyframe 2 position, the red brush is at keyframe 0 (base).

    You will probably think your mover only has sound while moving at the beginning. The sound is an ambient sound that occurs while it is moving, and the sound doesn't have a large radius. If you walk over to where the mover is headed, and wait for it to move BACK to its original spot, you will hear the ambient sound because you are close enough to it to hear it.

    Just keep in mind, you need to set "NumKeys" to the number of keyframes you want to use. I hope you get a LOT of use out of the mover, it is really an awesome tool in the world of Unreal. For some truly advanced use of Movers, look for my upcoming tutorial entitled "Advanced Doors".

    Good Luck


Reflective Surfaces

Reflective and Mirrored Surfaces

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    This is the third tutorial that I have done, so I guess I still have a hellova lot of work to do :)

    Abstract:

    This is a reasonably small tutorial (well at least I think it will be) that will show you how to make a surface reflectiveP>

    Assumptions:

    I am only assuming that you already know how to create a wall, floor, roof or anything that has a texture and can follow instructions... :)

    Tutorial:

    Introduction:

    The first thing to decide is where you will have reflective surfaces. I mean, you CAN have them everywhere, but there are a few guidelines to placing mirrors and reflective surfaces. These are simply here to make your level playable on something that does not resemble NASA and so that you will actually be able to see (I will explain this later)

    Where to put Reflective Surfaces:

    1. Mirrors are generally best placed on walls, as this means that anyone passing them !Should! see themselves.
    2. Partly Reflective surfaces (not mirrors, but say a marble floor with a high polish) seem to look best when they are used as a floor, as this gives a beautiful effect of reflectiveness - but not a mirror (have you ever seen a mirror on the ground?)
    3. When placing mirrors, make sure you don't place them ALL around a single area with lights in it, as they will all reflect the light, thus amplifying the light to a ridiculas level and generally SLOWING that section of the map to a ridiculas speed (completely unplayable on everything but NASA).
    4. Mirrors and reflective surfaces unfortunatley WILL slow your level down, so I would suggest that you use them sparingly - mainly for eyecandy - rather then using this as a main playing feature. :)

    Full Mirrors:

    To create a full mirror on a surface, select the surface and go into Surface properties (Right Clicking). Now check the boxes "Mirror" and "Unlit".
    1. "Mirror" will turn the texture into a mirror (Duh!) but this will also cause it to be uneffected by the lights around it, which means that it will be a black surface until something like a Blaster (the first gun, just in case...) is set off next to it. Then and ONLY then will it actually show up as a mirror to the light generated from the blast :(
    2. "Unlit" will make normal lighting effect the surface. I mean, if you have a nicely lit room, the mirror will look like a normal mirror... :)

    Partly Reflective Surfaces:

    Now we are talking floors, polished surfaces and dirty mirrors (the cool stuff that is...). Simply follow the same steps as above, but this time ALSO check the "Translucent" box under the Surface Properties. This will make the surface act like a mirror, but also allow you to see the texture that the surface is drawn with. This works VERY well for floor using the "marble" textures.

    Additional Notes on mirrors:

    Mirrors will not show up until you are in the actual game - I mean they will not show up as mirrors when looking through the 3D veiw of the editor... Don't worry this is perfectly normal and is there to make your editing life easier. As I said before, mirrors and partly reflective surfaces are best used sparingly, as they DO! Slow down the gameplay.


Natural Landscapes

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Natural Landscapes

    Natural Landscapes seem to be what everyone wants to do when they are creating an outdoor level, so I best be having a little talk here and talking about some of my own methods of acheiving a good looking level. I do have a few tricks here that I will explain, but a lot of it is simple technique that has been used and grown from simple editing. I mainly use a technique that grows slowly using the basic primitives and then manipulates the brushes, and adds in more brushes until slowly the main object that we are trying to create gorws to look natural becuase of the irregularity of the brushes, then once that has been done to a level that we are happy with, an intersection of the entire brush will be made and the new builder brush is used to create a realistic looking mountan or cavern or whatever in the place where we want it.

    To start with I think that an example of how I created a damn good looking cavern will do the best job of explaining all the rambling that I have done so far. I needed a cavern to look like a natural cave in one half of it (The level is actually for the up and forthcoming Mionions of Evil TC). I needed a cavern with a realistic looking rock-fall and a waterfall flowing over the rocks and into anali-made pool of water. I started off deciding just how big my room would have to be, then added a little "play" on each side so that I would be able to add in extra crevases with a realistic amount of ease, rather then toying with additional brushes that would have to be subtracted. When I had decided upon the actual size of the room (about 1024x2048x1024 if you are really interested) and I made this room off a little from the level. This was then where I was working the most with all the additional brushes that I would be using. It is much better to create something like this off from where it will actually be going, as it then allows you to "test it out" by doing an intersection and putting it into the level before you have to delete any of the older brushes. Ok, getting back to where we were. I then created a few "base" brushes. I wanted the rocks to look as natural as I could, so I made a basic cube brush, then intersected it against the wall of the room, until it looked fairly realistic. This was actually fairly easy, as I simply rotated the brush so that all the main angled sides were overlapping against the wall, then did an intersection to get rid of it, and rotated it some more, then did another intersection and so forth, until the brush no longer looked like a cube, but a boulder. When it did look like a large rock, I put if off to the side, and added it to the world, so that it would not go anywhere and I could use it over and over again if need be. I then made another cube and followed the steps above until I had another boulder although I made sure that it looked different to the first that I had made. I kept this up, until I had made about four or five (I can't remember exactly how many) and they were all sitting nicely off to the side of the room. When I was satisfied with what I had for base brushes, I "drew" the entire inside of the cave inside my head.

    Then with the brushes that I had made, I started to build the cave little by little, and made sure that I intersected the brushes EACH time before actually added it to the world. This was quite important, becuase if you didn't do this, then you would actually be causing horrible BSP holes (If you are not sure what these are, please look up the Jargon tutorial) and then most likely create a HOM effect. Slowly as ech of the brushes was added, the cave interrior was begining to look more and more like a real cave would look. To stop the repetition as I was only using a few main brushes I was rotating the side that would be visible all the time, and also changing the size of the brushes so that if I did have the same faces of the brush showing, I would not have them the same size as well. Even though this was fairly tedious and did take a long time for it to be as good as I wanted it to be, the final product was nothing short of "real". When I had finished createing the actual rocks that would make the "fallen in" section of the room, I was ready to start adding in the pool and the water. Using the 3D view of the editor, I made completely sure that I would have no holes in the bottom of the pool and the sides of the pool, so that I would be able to create a sheet of water, and safely place a water zone below it. I had to "patch up" a few holes that were there, and I also made (I got a little carried away here, and I admit that freely) a little rock fall or "rapids" for the water to flow into, then finally I made sure that the "water level" would be as natural as possible by adding in a few extra rocks to make sure that the pool would look as natural as possible. When I had made the actual sides and interior of the pool, I added in the nali-made pool edge, which was made from a cylinder (I think about 40 sides) and hollow. I made it to be an elongated shape by simply streching one plave of the shape so that it would grow longer, but nothing else. When I had the right shape built, I intersected the brush and found that I had the brush just to the exact dimensions so that the water would flow easily and freely into it like a real water would flow. When this was complete and I had the brush textured and just what I wanted it to look like (still without a drop of water though) I did an intersection of the entire room, and placed it into the level where it was supposed to go. I then saved the level ( Something that you should do as often as possible) and started to work out how to create the water itself.

    Who ever said that water had to be made from a sheet? I think that this is a great misleading theory that everyone seems to have. I actually used a cube and like I was making a boulder, slowly intersected to and from the brush until I had the red builder brush looking just like the water would look when I had finished. This was done in a few steps, as it was simply too much work to create the entire brush from a single brush, so I made a few brushes and created them in a way that they would overlap each other, and the player would not be able to see where any of them did actually meet. The fact that I was using a solid brush here also made the job a lot easier as it allowed me to work within a finite space and not work with a sheet that would intersect poorly, if not all the edges were surrounded. I finally managed to create the brushes that would make the water and placed them into the level just like I would place water normally. I prefer to create water so that it actually flows, and so I went to each individual facet of the water, and made them "flow" downward, even though thus did take a lot of playing about and changeing the individual directions of the faces and changing the panning scale. Finaly when they all looked good enough so that I would be happy with them, and then I placed in the water zones where I knew the player could reach without cheating. I actually did leave a few places there that were not filed with water, but this was simply becuase it would not be possible for the player to get to them and so placing water into them would simlpy add to the resource list that would have to be filled, and it would slow down the level just that little bit more.

    Finally I added the lighting to the the cavern and did a final rebuild and then then cavern was complete. There is no "one" way to do natural landscapes and rocks, but this should be a good and reliable way to create very realistic looking rock formations that will look really good in your levels. If you want to add to this tutorial, then please email me, and I will happily add useful comments to this, and there is simply so much to cover, and what I have done here is simply one method of doing rocks.


Pathnodes

    Sharky
    jneta@ez-net.com

    First off i hope you know how to use the editor and understand the basics of good design and wepon placement. Also you should know how to add actors to your level.

    First, take a level that is setup the way you want it to look. Make a backup of you level. Then goto classes and under NavigationPoint find pathNode. You start adding path nodes by figuring out the best path through you level. Start placing them far enough apart that they are evenly space but close enough that they are visible from one another. Also try to go past the wepons in your level (when you generate the path it will connect to the wepons). Now that you have all your nodes evenly spaced go into the 3d view and "walk" through your level and make sure they are visible as you go from node to node.

    If you have any elevators/lifts in your level you must place a lift exit where you want the bot to wait for the lift/elevator and a liftcenter where the lift is on the ground. Next you assign their lift event to the name of the mover's tag.

    If you have any Portals in your level you must place a node inside the portal on each side or the bots will not use the portal. Im not sure about how teleporters work yet but ill add to this once i figure it out..

    Any doors you have in your level should have path nodes right next to the door if you want the bot to use the door.

    Also if you have any dead end hallways the bots will probably not go down them unless there is a wepon at the end

    Now that everything is setup as you want it. Hit f8 to bring up rebuild and goto the lighting tab and click on generate path. Rebuild your level and you should be all set to play it with bots.

    Well thats it for this edition of the path node tutorial... i will add more to it when i get a better understanding of how they work and any little tricks i figure out about them will be added


Portals

    Sharky
    jneta@ez-net.com

    Where to start? well first off make a basic level.. if you dont know how to do this read a basic tutorial (ill be writing one soon).. ok you have your big cube level... add your player start and some lights... next make your builder cube 200x200x200 and subtract it from a side.. next subtract from another wall (see pic1).

    The test room with the holes
    The test room with the holes Now select sheet builder set the properties to Y-Wall(or X-Wall depending on the orientation of the holes in the wall) and set the U and V to 200.. this will act as a barrier for your portal and the rest of your level.. move it in front of one of the holes in the wall (make sure its lined up perfectly).. now select a nice lookin texture (i suggest picking from the GenWarp set) now that you have the texture you want hit the add special button (pic2) and use the same settings as in (pic3)...

    HIT THIS BUTTON

    USE THESE SETTINGS
    Move the builder brush to the other hole and repeat... Next goto classes and pick ZoneInfo->WarpZoneInfo and add one inside each of the holes you made... now set the properties of one of the WarpZones to Dest of Warp1 and Src of Warp2... now pick the other WarpZone and set it to Dest Warp2 and Src Warp1 (see pic4).

    The Property box(double click the ZoneInfo to pop it up)
    Now rebuild and your all set.. save your level and test it out

    The final level in UnrealED

    The level in the game, note the hall of mirrors effect (its easier to see without a texture on the sheets)


Portals 2

    Isolation

    The Basics

    Lets begin with a simple room, I am going to use the exact coordinates I used for my Portal map for this tutorial. The room was a 400 unit height, and 600x600 on Width and Breadth. Create the room, texture it, and throw in a PlayerStart. Now we will add two boxes. This is the space where the Portals will be. Thanks to Inoxx from the Unreal team the portal boxes HAVE to be 64x64x64 or bigger. For this little map we are making make it 200x300x200 (height*width*breadth). Put it on the outside of the room but make sure the end of it is touching the room then hit on the subtract from world button. Now let's change the brush marker to 200x200x300 this will show the same cube but now to the side. Put it on another wall and extract the world (just like the figure). One thing to remember both of the portals have to have the EXACT same geometry as the other. One CANNOT be bigger than the other. Please make sure both of your portals are done correctly.

    The Portals

    Now we will create a sheet to do this click on the button all the way on the bottom of the toolbar that looks like a gray diamond. This will change the marker to a thin sheet, now right click on the sheet button and go to Sheet Properties make the Orientation Y-Wall to make it vertical and then the U value should be 300 and the V value should be 200, hit build. Place this sheet in front of the portal that it fits and then click on the Add Special Brush button and on the PRedefined drop down list, pick Zone Portal and your portal is set. Now go to the Sheet properties and change the values from 300x200 to 200x300 and then the sheet will change direction to the other portal. Put that on the cover of the portal like the figure and hit Add Brush*. If you did it correctly it should look like the next figure. *- If you don't see anything please Rebuild geometry and if you still don't see anything make sure you added the brush.

    The WarpZone

    Now we will link the portals together. Go to the Classes menu -> Info -> ZoneInfo -> WarpZoneInfo. Add a WarpZoneInfo inside each of the boxes make sure they are in correctly at the X and Y values. Now Go to the WarpZoneInfo properties by right clicking on the WarpZoneInfo tag (it looks like a cube with a question mark) and goto WarpZoneInfo. You will see two boxes OthersideUrl and ThisTag. Double click on Othersideurl and enter the value warp1 and then double click on ThisTag and put in the value warp2. Now go to the other WarZoneInfo in the other portal and go to its properties. Do the same thing as you did with the first one but this time the Othersideurl will be Warp2 and ThisTag will be Warp1. Close the box and you are two steps away from having portals.

    Surface Properties

    Now click on one of the sheets we put in before, right click it after its been selected and go to its properties. Check Portals, 2 Sided, and Invincible. Hit close and do the same to the other one. Now to test that your portals are correct click on the joystick so that its not a shady white. When you do that the sheet brushes should not be visible instead you should see right through them and see the boxes with the WarpZoneInfo tags in them. If you do see the boxes you can check that the portals are there by clicking Mode and Zones/Portals, the whole room should be one color except the portals should be different. If this is done correctly you are almost there. Just add lights around the map and then rebuild Geometry, BSP, and Lighting. Play the map and see if it came out right.

    Mini-Portal Faq

    I click on the joystick and I see black wholes in the map.

  • Rotate the sheets so that the side that's facing the room is now facing inside the box and vice versa. It could be both sheets or just one of them. Experiment this has happened to me before and I changed the the sheets around a bit. After you do rotate it make sure you rebuild the geometry and then see if its still messed.

  • Another problem could be that the boxes aren't the same geometry I have never had this problem but you should make sure they are correct.

  • Make sure that the sheets are covering everything on the portal so you can't see inside. This could be a major problem

  • Make sure that the WarpZoneInfo entities are inside the portals boxes.

    I play the level and I have portals but they are messed up I see myself but 3 times bigger.

  • This also happened to me before, I fixed this by rotating the sheets again. If anyone knows why this even happens please email me.


Publishing

Publishing your Level

    cyberg][dan
    dan@catapultic.com

    Foreword:

    This tutorial should successfully show you how to publish your finished level in a professional way, with screenshots and all. You won't even have to distribute your texture package.

    Abstract:

    Screenshots and Level Info... two of the most important but most often ignored parts of UnrealEd level creation.

    Assumptions:

    I assume that you have created a level and are completely happy with the geometry, textures, lighting, and overall playability. You should feel ready to put it "out there."

    Introduction:

    I'm sure you notice the level screenshots before you select a level to play when starting a game. It also provides you with the author's name (or alias), recommended number of players, and level title. You've also probably wondered how you can implement that in your finished level. This short tutorial will show you how.

    Steps To Include a Screenshot:

    1. Make a backup copy of the level you're about to wrap up, just in case something happens. Better safe than sorry.

    2. Start up UnrealEd and open your backup copy.

    3. Now take a really good screenshot of your level. This is *always* best to do in spectator mode, so you include more of your level and less of the HUD. Press F11 to bump up the brightness to a desirable degree and hit F9 to capture the screen. The screenshot will now be in your <TournamentDir>\System folder as a raw BMP file.

    4. Now, open up your favorite *quality* image editor. You need something like Photoshop or Paint Shop Pro to handle the color depth conversions without pixellation and quality reduction.

    5. Open your screenshot bitmap file.

    6. Resize the image proportionally (or whatever works best for the image) to 256 by 256 pixels.

    7. Decrease the color depth to 256 colors. Save it and go back into UnrealEd.

    8. Choose Textures from the Browse menu on the side. On the bottom of the screen, click the Import button.

    9. Choose your screenshot file and open it.

    10. You will now get the Import Texture dialog box. Fill in "Screenshot" for the name, "MyLevel" for the package, and uncheck the "Generate Mipmaps" text box. Your dialog should look like the one below:

      Your Import Texture dialog box configuration

    11. Click OK. Your screenshot will appear in a blank package in the Texture window.

    Whew!

    That wasn't so bad now, was it? But wait, we're not done yet. We just have our level screenshot ready to use.

    Setting LevelInfo Properties

    1. Now, navigate to Options menu -> L evel properties... or hit F6 to bring up the Level Properties dialog box.

    2. Expand the LevelInfo node.

    3. You should now fill in the Author, IdealPlayerCount and Title fields. Author can be whatever you desire. IdealPlayerCount should be a single number or a general amount, such as 8-16. Title should be the name of your level--not the filename, but the title (e.g., DM-Peak's title is The Peak Monastery).

    4. Now you need to set the DefaultGameType. Move to this field and press the ellipsis button (...) to move over to the Classes menu on the Browse toolbar. Navigate to *Info -> *GameInfo and within those nodes, find the game type your map is designed for. Click the game type once and move back over to the Level Properties dialog. Click the Use button in the DefaultGameType field to fill the field with your selected game type.

      *Note: All the Unreal game types will be found in UnrealGameInfo and all the UT game types will be found in TournamentGameInfo.

    5. DefaultGameType is taken care of. Now move down to the Screenshot field and click the ellipsis button again. The Browse bar should load up the textures. Select MyLevel from the second drop-down list. Click on your screenshot texture (it should be called Screenshot) and move back to the Level Properties dialog. Click the Use button in the Screenshot field to fill in the information.

    6. That's it! You're done for now. Below is an example shot of the filled-in LevelInfo node:

      Level info

    Publishing To The Web

    Now that you have everything filled in, rebuild your map (as always) and save it. It's generous to zip your .unr map file with a .txt file describing release and copyright notices, as well as contact information.

    You now have a finished map, ready to be sent all over the world for Unreal players everywhere.
    Here's a few addresses of places that will (currently) playtest, review, and/or distribute your map:

      NaliCity FTP or PlanetUnreal/NaliCity E-Mail: Use these links to directly transfer your maps to the greatest distributors of maps of all time!

      Jeff Earnshaw: Jeff is the webmaster of the UT Underground (http://www.angelfire.com/ut/ground) and needs your good maps on his site! Send them in and he'll get a review on it too.

    Well, that just about wraps it up. I hope you've learned a bunch about zipping your level up cleanly and professionally.

    Have a great time out there, and if you have any questions, feel free to e-mail me.


Skyboxes

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    I have been asked quite a few times about skyboxes, so this tutorial should hopefully explain just what is the go with em ;)

    Assumptions

    There is nothing really all the hard about skyboxes, so I am assuming that if you want to build a basic skybox, then you should easily be able to follow what I am talking about here.

    The Theory

    A skybox is simply another place in the level that will show up as the "outside" edge of your level. You will often see a nice level that is based in the underground, and with a few places that show light (or the sky) and yet they all look identical, and seem to actualy be made of the same sky. This is because the thing that the player is seeing IS the same peice of sky. A lot of people who start making a level, and try to have a nice sky will try to create a Sky for each part of the level, but this is certainly the wrong thing to do here. Wouldn't it be so much easier if we were able to create a little "room" with a sky in it, and then simply click on a property of a texture, and say "Don't show this texture here, but show what is inside the little room with the sky in it". ? Well that is basically just what we can do.

    The Sky Itself

    To make the sky itself is probably the hardest part. Sure, it's easy to make one that will do the trick, but it's a lot harder to make a really good looking sky that is worthy of a real level.

    There are a few guidelines that you should stick to when you are making a good skybox.

    • Size
      The size of the actual skybox is the most common mistake that is made by people when they first start. Most people assume that the skybox will have to be large enough to cover the entire "sky" of the level itself. This is not the case at all. It is a norm to make the skybox about 1024x1024x768 or that is the size that should work well with most levels. This is large enough not to get too tricky, but at the same time, it's also not too large to start getting messy with lights and the like, and will not hog too much system resources when the level is being played.

    • Shape
      Did anyone ever say that the shape of a skybox HAD to be an actualy "box"? No! I do often use a box for simplicity, but if you have a level that will only be showing one side of a skybox, what is to say that you can't make it in a wedge shape, or perhaps a nice spherical shape? There are limitless possibilities that you can use here but I just wanted to piont that out, as it seems to be a "assumption" that most people make - wrongly.

    • Lighting
      Lights are of course very important to use in a skybox, and there are a number of things that you will want to consider before you start throwing them in.

      • Dynamic Lighting : While there is nothing to say that you can't use dynamic lights in the skybox, and they certainly look really nice, you will find that they are actually rarely used in the main Unreal levels, as they do hog resources like little else. Think about just how much resources Dynamic Lights can use when they are in a level itself. If you use one that is slightly larger you will notice that it will really slbog down a smaller machine, and the same goes for their use in a Skybox, although it is actually multiplied somewhat. When you place on in a skybox, it greatly increases the amount of resouces that will have to be assigned to it, and can really slow down a smaller unit when the person is playing. This is one of those tricky things however, because if the level generally plays VERY well, and the skybox will only bee seen from a place that has little "happening" around it, then sure, throw something nice in and give the players a little more eyecandy, but if you have a huge open (or outdoor) level where the skybox is seen from a lot of places and there will be fighting etc, then you might want to hold off the eye candy and simply go for better playability.

      • Fog : Please don't use fog in your skybox. It won't work. It will however still slow down your level quite a bit. The reason that you level will slow down but the fog won't be seen is simply becuase to see fog in a level it the player HAS to be in the same Zone as the fog. Now if you place fog into the skybox, you will still have the fog there, and if by cheating the player actually "flew" into the skybox, then technically yes, it could be seen there, but if the player is normally playing the level, they would of course not be in the same zone as the fog, so it would not be seen at all. However becuase the fog "technically is there", the PC still has to do all the horrible calculations for it, in the case that the player was to fly into the area. Once again, I am simply going to say Don't!

      • Colored Lighting : Colors will normally add in quite a bit to make your sky look a lot nicer, and it will also not hog too many system resources. Most good skyboxes (You know the ones that you stare at for ages, until some monster creeps up and shoots you, at which point you have to "snap out of it" and start playing again) will use quite a few colors, and you might even hink that they are somewhat "psychadelic" if you look at them for too long, and try to "work them out", but if you simply look at them, then you will think "Yikes, what a lovely sky there is here...". That is the effect that we are trying to get here. Feel free to use lots of nice colors in your sky, and if you want to have a level that looks realistic rather then painting it with splashes of many colors, why not create a sunset, or sunrise and have the sky painted with a myrad of yellows, organges and reds.

    • Movers
      You can place movers into your skybox, but once again, it can be a little straining on resources and keep in mind that whatever you put there, will most likely be MUCH larger when you see it in the actual game. However a good use of a mover in the skybox, is a VERY good way to add in that little "extra" to make your level stick out above the normal that most people are putting out. For example, if you have a good level that is set ina futuristic timeframe, why not create a space ship, or fighter plane (whatever) that is triggered by something in the level, and it flies across the sky, then dissapears not to be seen again. If you have played Quake 2 (I guess that most people have done so) you wil most likely remember the first level, when there is all sorts of braodcasts going on, and you can see and hear the fighters as they fly over the base, then are not seen again. This may seem like a LOT of wrok for one simple thing that will never be seen again, but the "shiver" that most player will get, will be worth the work you put into it.

    The way to make a skybox.

    I have left this to last, as I really wanted you to read all of the stuff that I have written about them first, and to keep it in mind as you are creating your own sky box, as all of the stuff there has been learnt from many hours both playing and editing levels, and it would seem like an awful waste for you to read this bit first, then miss out on all of the "little tricks" that I have written there.
    Ok enough crap here on my part. You have waited (and read) enough. Here is the go...

    A skybox is a simple thing to create. All you need is a "room" (which as I said, can be a box, and sphere, whatever) as long as it does not have any holes in it. When you have made you sky to look like whatever you want, click on the class browser and then open the zoneinfo menu (click on the little + sign next to it). Choose Skybox info and then place that into the skybox, by right clicking in the 3D view and selecting it from the drop menu.

    When the zoneinfo is in the skybox itself, all you have to do, is to open the "advanced properties" of a texture, and then highlight the "fake backdrop" option. This will not appear to do much at all. It will only work in the actual game, or when you test it out from within the editor.


Vertex Editing

    Mark Poesch

    1) Within UnrealEd, select a wireframe render mode, or use a window already in wireframe for vertex editing.

    2) Select the brush to modify by left clicking on an edge (this will allow you to see the current pivot point).

    3) Select the desired vertex with a left click (right clicking will select the vertex, and snap that vertex to the grid -- moving the entire brush relative to the vertex).

    4) With the target vertex selected, hold the Alt key, the left-click-and-drag the vertex.

    NOTES:
    - Grid snapping behavior is still not consistent -- turning off the grid-snap, selecting the vertex, then turning on the grid-snap, then moving the vertex usually works when nothing else does.

    - Vertex editing will snap to adjacent vertices -- this is intended. Unfortunately, there is no good way (currently) to separate the vertices once they're aligned. (Vertex editing affects all vertices within the SnapDistance radius simultaneously.)

    - A "too few vertices" error can cause the editor to crash if too many vertices are edited into the same plane.... I wasn't able to track down the bug before Unreal shipped. Sorry.

    - The current implementation of vertex editing is not production quality (hey! I got it working in less than four hours. :) Make sure you save your work before you use the feature.

    Mark Poesch
    Legend Entertainment


Water

    Isolation

    Creating a Pool

    Lets begin with a simple room already done. Add in a PlayerStart entity (Classes -> Navigation Point -> PlayerStart) somewhere inside the room. Now with your Brush Maker mark where you want the pool to be. Make it about 400x175 with 200 units of height. Put it on the floor of your map (top of the marker hitting the floor), then hit the Subtract from world button and viola you have your pool (with no water ofcourse).

    Adding Water

    First lets load the liquid texture set genfluid.utx* is fine, this will be needed later. Now with that done hit the Build a Sheet button (button with the grey diamond) and your red marker should become a thin line, right click the Build Sheet button and go to Sheet Properties. Orientation should be Floor/Ceiling and the U and V coordinates should be the same size as the pool you made before (400x175 if you used the same numbers as I mentioned before) hit build and you're set. Then align it correctly with the pool and put it a bit lower than floor level so it doesn't look like it's going to overflow. Now pick the water texture you wish to use and then click on the Add Special Brush button (four down on the right of the toolbar) on the predefined dropdown list pick water. Thanks to the Epic boys the hard part is done for you, the correct water settings. Hit Add Special and you are set. You should see a nice transparent brush in side the pool if not right click on the grid go to Rebuilder (F8) and in Geometry hit Rebuild Geometry (make sure that Auto BSP is not clicked). Bam! You have your water brush.

    Water Volume

    Ok now you have the top of your water now if you play your level you will see you can't swin around that is because we haven't added the Zone to make it seem like there is water. Now go to the Textures dropdown list and pick Classes, go to Info -> ZoneInfo -> WaterZone. Click on WaterZone and then right click on the grid where the water is (this should be BELOW the water texture) make sure it is correctly inside the pool. After you hit Add you will see a question mark with a cube. Now goto Rebuilder (F8) and Rebuild Geometry then go to the BSP tab and Rebuld BSP make sure Auto Lighthing is not clicked. Lighthing is optional in your case. If you want to go ahead and throw some lights in this shouldn't affect the water. After rebuilding Geometry and BSP save your level and play it. Viola you have a nice cool refreshing pool!

    Make it Alive

    Want to make your water move? Right click on the water texture and go to Surface Properties. You will see Translucent, Portal, and 2 sided checked, leave it checked. Now check on Small Wavy and click on close and there it should be done.

    Adding Sound (Optional)

    Your pool not good enough silent? Fine then right click on the WaterZone entity (the question mark and the cube inside the pool) Goto Properties and click on Sound. Click on Ambient Sound and it will say None, then click on the three periods and it will open the Sound list on the right for you to choose. None of the ambient sounds are loaded when you start up UnrealED so hit on load and choose the AmbOutside sound package. After it opens choose any of the water sounds you feel like picking. Then hit USE on the WaterZone properties menu and now your water has sound.

    Ending

    That is basically how you make your body of water. Experiment and mess around and you will learn a lot that way. It isn't hard at all it just takes time. Slime and Lava is basically the same just different Zone entities, if Lava and Slime tutorials are needed head over the feedback page and tell us.

    * - For textures that are animated already open the Liquids.utx. Then the textures don't need the surface properties of Big Wavy and Small Wavy. Thanks Rip.

    - Final water hole in Unreal -


Advanced Water

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    I did the intermediate water tutorial last night, and I want to get all the cool things that can be done with water off my chest...

    Abstract:

    This tutorial will pick up where the last water tutorial left off. I will explain how to make water surfaces move, how to place multiple water "openings" and how to make things jut out of the water level without spoiling the rest of the surface - and anything else I can think of while I am typing...

    Assumptions:

    I assume that you know a fair bit about UnrealED by now, so I won't go into as much detail about where things are - just how to do things...

    Tutorial:

    Introduction:

    Now that you have mastered what was in the first water tutorial, you probably want to add more cool water effects into the level, so here goes.

    Adding more then one exit from a water source:

    This is actually quite simple, to have a water source with more then one exit, all you need to do is create the "hole" for the water to go into, and then place the required water "sheets" (I mean the water planes)and then add a water zone in between them. It is really not an advanced step, but most people NEVER tell anyone that it is so simple...

    Making the water surface "move":

    Once again, this probably could go into a simpler tutorial, but I didn't put into the first water tute, so it goes here... If you don't want a surface texture from "liquids.utx" (they will bog down slower pc's - or for whatever reason) but you don't want the plain simple water textures that are found through the rest of the textures, then select one (of the plain ones that is) and when you have placed it where you want it to go, select the surface (top and bottom) and bring up the advanced texture options. Click on "Small Wavy" or "Big Wavy" and it will move from side to side, and up and down. This adds a lot of realism, but won't chew nearly as much resources as an animated texture...

    Waterfalls:

    Waterfalls are really cool. A waterfall does look best when it does fall into a pool, so when you have a pool - and a nice ledge, crevice, cranny (whatever) for the water to come from, create a solid cone or a rectangle (whatever shape will look best for your setting - but do it using the "sheet" tool) and select the add special brush as you would normally do, select the water plane (as you would normally do) and then place a "waterzone" in the middle of the shape. If you build it well enough, you may not even have to use two separate water zone, just use the one from the pool of water. Now select the sides of the water, and bring up their texture properties. Under the "scale" section, make sure that they are stretched going up and down, then go to the first tag and make them "pan" going down. If your waterfall is in a cave, you might also want to give it a fair amount of fog around the base, but if it's out in the open, then go easy as it may drain a lot of system resources. Waterfalls should not allow players to swin up them, so you will need to either make it a narrow size, or alter the gravity setting under advanced properties. A setting of a million (1000000) I think ( I hope I remeber correctly) should do the trick, alternatley set a gravity that pushes a player to the front or back of a waterfall. Lastly waterfalls need a waterfall sound, Furthermore, you should put a ambeint sound in the "waterzone" AND around it. This will let the waterfall be heard from a distance and whilst a player is underwater.

    Protruding objects:

    This is easy, but it requires a trick to perform. That is, the water plane MUST be added AFTER all the rocks (or whatever) are put into the water surface. I mean, if you have a pool and you want to place a rock or two jutting out above the water level, you have to place the rocks into the "water" before you actually place the water sheet into the pool. You will also HAVE to deintersect the brush before you add it into the map, otherwise, you WILL get horrible effects.


The Water Engine

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    This tutorial will explain how to use some of the features of the "water engine" and show you how to create animated water textures in making a water texture that is animated on a water texture that you either make in a third party software oackage, or the simple editoing of a texture that come with Unrealed.

    For once I am writing this one at home, so I will be attaching a few good images with it,and I actually (wow) have the editor here so I can call up anything I like. I will start off in a quick little paragraph about how to make this base image. Ok, I am using a copy of PSP5 (Paint Shop Pro 5) as everyone can download a trial version and do exactly as I have done here. I have made a basic empty image 512x512 and used the spray can tool to make a base background a nice shade of blue. When I was creating this base color, I used the "ocean" setting to make it a little more "realistic" and less mundane. Then I made sure that the image was single layered, and had no transpericy and made a selection from the middle of it that was about 3/4 of the image size, and perfectly square. Then from the selections menu, I chose "Convert to seamless pattern". When the new image showed up, I made sure that the image was 256x256 in size,and cut the colors back to 256. Having done that, I saved the new selection as a .pcx file (Ver 5) and closed PSP.

    Ok, that is one part out of the way. Into Ued :) I went to the "browse bar" and opened textures. (Ie chose "textures" from the drop down list, that contains "Textures, Classes, SoundFX, Music" ). Then opening the "import" function right at the bottom of the tool bar, I found the file, and imported into Unrealed.
    The import button is shaded a light green here, to make sure that you are using the right one. Ok, in the next window, I have selected the right file (remember the one that you just made in PSP?) and in the next window I made the following selections.

    Ok here is what all the bits mean. The Name is what the texture will be called as a texture. The Group is what part of a package the texture will be situated in. You know like you can open up the "Ancient.utx" file, and it is split up into various catergories, like "Floor, Roof, Wall" etc. Well, that is what the group section decides. Import the image into a Group called "Still".The Packacge is what the actual name of the package when it has loaded. Enter "Water_tute" for this texture, so that everything will be the same for easy reference in this tutorial.

    Do not change any of the other settings there, as they will not be required for this paricular texture. Ok, assuming that everything went according to plan there, you should now see a nice little thumbnail of the texture that you made in PSP and it should be in a package called "Water_Tute" in the group "Still".
    This will be the still version of your water. In every map that you do, try to include a still part of the water, so that it allows the player to seethe difference between the still and moving water. I mean a puddle would not be aminated (Unless you have stuff drippinginto it Hehee).

    Cool easy so far innit? Hehe, that is the hard part...

    Ok, now click on the "New" button (Right near the import button in the textures browser) and in the window that pops up, choose the following selections.
    Name = Anim_1 : This is what the texture will be called, so it is a good idea to put the "anim" somewhere in the name for easy refernce later on...
    Package = Water_tute : This should already be set, but just in case if it is something else, callit the same as you did earlier.
    Group = Aminated : This should default to "Still" but change it to animated so that you can locate the texture easier later on. It is a good idea to store different textures in different groups (Hopefull have then arranged into some resemblence of order as well).
    Class = Wet Texture : This is where you will find thetools needed to create the particular texture that you want. It is a dropdown menu. Select the "Wet Texture" and leave the Size at 256x256 (It should be at that size to begin with). Click on the "Create the Texture Button" and start to make the fun stuff.

    You should see a nice large BLACK square and a Properties box. In the properties box, drop the "Water Paint" sub-menu (Click on the little + sign next to the name) and select "Source Texture" from it. Having your new package open, select the "water" texture (You know the one that you just made in PSP) and pressthe little "use"button in the SourceTexture menu-line.

    Your large "black box" should now have the image of the texture that you just did in PSP in it, and you can finally start on the fun stuff.

    Where you see "Drop Type" in the WaterPaint sub-menu, click on DROP_PhaseSpot and you will see a huge drop down menu.
    These are the cool water and wave effects that you can use in the texture to make it look like real water. Here is a quick list of what they do :

    • Drop Fixed Depth - This will make a little "dint" in the water that does not go up ordown,but just stays at the level that you set it to. I findthisone quite useless.

    • Drop Phase Spot - This will make a little drop in the water,that i just like having a drop of water falling into one part of thetexture all the time. It will also keep a nice little circular wave being formed around the drop center.

    • Drop Shallow Spot - This willbe just like the "Drop Phase Spot" although itis much smaller.

    • Drop Half Ampl - This is once again similar to the "Drop Phase Spot" but it uses exactly half the Amplitude that you have set (Explained later). The size is very close to the "Drop Phase Spot"

    • Drop Random Mover - This will make a little pattern ofwaves dance all over your image. It follows no pattern, and actuallylooks quite like a little water bug running about on the surface of the water. Very cool and useful.

    • Drop Fixed Random Spot - With that name, you ight expact it to move about the texture, but it actually stay almost in the one place, only moving a little bit to the sides, so that it willmake the waves collide with each other, thus making them look much more realistic, then if it were just in the exact same place each time.

    • Drop Whirly Thing - Damn, I think that name is soooo creative.... ;) Anyways this setting will create a little "drop" of water that moves about in a small circle (not a regular one though :) and looks very much like a little vug once again that travels about on the water.

    • Drop Big Whirly - Same as above, but bigger andmakes bigger waves.

    • Drop Horizontal Line - Make a line that will go down into the texture. Ifyou place just one of these, it will most likely look like it dissapears soon, but if you place a LOT of them all over the texture, then you will see that it makes the texture look like it is all moving.

    • Drop Vertical, Diagonal 1 & 2 Line - Same as above, but the wave is in a different direction.

    • Drop Horizontal Osc - This will make a line wave (Like the above) but will actually keep the wave moving up and down allthetime, sothat it will akegreat looking waves on your texture. Very useful if you placeeven just a few on the surface, and let their waves meet and collide.

    • Drop Vertical Osc, Diagonal 1 & 2 Osc - Same as above, but in a different direction.

    • Drop Rain Drops - This will make a random drop fall in the general area of where you click the mouse. Very nice and useful.

    • Drop Area Clamp - This will "raise" a part of the texture and make it look like there was a pipe under it pushing water up into the surface of the water. I have found very little use for this one.

    • Drop Leaky Tap - This will make a drop fall into a set place (where you click) and it will keep dropping there in a steady and "leaky tap" like manner.

    • Drop Drippy Tap - Damn, who makes up the names eh??? Anyways, this is just like the one above, although the drops seem to come about twice as fast. Very useful.

    Right that covers all of them.

    There are a few othersettingin the screen though, so I better explain them eh? Ok. Another list methinks ;)

    • FX Amplitude - This does not seem to do anything at all :( Please correct me if you find it to do something, but I have not noticed any changes between 1 and 255.

    • FX Depth - By the name, I would guess thatit denotes how deep a particular wave will go, but once again, I have found it to do little.

    • FX Frequency - This sets howmany times your particular will be shown in each cycle. I mean the higher the number, the more waves will be generated in a period of time. They will be no bigger, or cover a large area, but merely more of the willbe made in the same amount of time.

    • FX Phase - This one doesn't seem to be doing anything right now, but I have used it before to change the speed of the actual animations ofthe waves that are created (although that wasin another part of the waterpainting program).

    • FX Radius - This will not work with all drop types, but I think thatn in general, you will be able to use it for "semi random" drops like the Rain Drops wave, to restrict the size of the area effected by it.

    • FX Size - Similar to the above. There are times that it seems to work, but other times it does nothng (like now) so I am guessing that it will only work for certian wave types.

    • FX Speed - Once again as the above.

    • FX Time - This will determine how long cetain waves functions take to finish. It doesn't work with all the wave types though.

    • Wave Amp - This decides how "deep" the wave itself will be, and how far up and down it goes. The bigger the number, the bigger the wave made will be. Seems to work for all the wave types.

    Ok Now that one is outta the way as well :)

    To use the different wave types, simply click on the image map when you have the right wave type seleceted and it willbe placed there. Really easy innit? Hehe, I told you making the right texture wasthe hard bit.

    When you have made the waves on the surface of the image Simply close both boxes and you will have the new texture ready for use in the level.

    One more thing, and this would be the most important bit. Save your package to the Hard drive. Heh, you might think that you already have the package saved, but this is not the case. Please for the sake of your textures, make sure that you save the file as soon as you finish making a new texture for it. To save the .utx file, press the button called (suprisingly enough) Save. Then you will be able to name the package file that you can then load into any level that you like, and you will also be able to send the filesto other people to use.


The Wave Engine

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    The wave engine in the editor is a great thing that does not seem to have been used in the actual Unreal Game. I have made quite a few water textures using this method, and I think (so do all the people that have seen them) that this type of water is much more realistic for outdoor areas, and has that "real water" feel to it. It will also make the impressiont aht the water in the game is moving up and down and has little waves all over it. Well enough blabbering, time to get to the tute eh?

    Opening up the Wave Engine

    Ok, this is really easy. To open up Wave Engine, all you have to do is to go to the "textures" browser, Click on "NEW" and you will see a little dialog box appear. There are a few options here.

    These options are basically what you will need to get you in. Here is a list of what each "setting" will do.

    • Name : This will determine the name of the texture when you have the texture package (.utx file) open in the editor.

    • package : This will be the name of the texture package when it is inside the editor. Note that this is not the name of the actual file.

    • Group : This will be the name of the group that the particular texture belongs to. You know how you can open up the Unreal textures, and they are nicely divided into things like "Roof, Floor, Walls" etc? Well that's how it's done. Enter the name of the group that you want the texture to be in. If you want it to be in a group that already exists, just enter the name of the group (Case sensetive) and it will be there.

    • Class : This is the type of class that the texture will be. For this tutorial, make sure that you have "Wave Texture" selected, otherwise you will have serious problems tryin to follow what is going on ;-)

    • Size : This is going to be the size of the texture that you make (in pixels). Most of the time, you will want a larger texture, although you could of course use a small one (say 64x64) if it's going to be repetitve, or placed on a small item. I would however suggest that textures to be made for general purpose (and certinaly for this tutorial) are at least 256x256 large ;->

    • Create this Texture : Well, this is the wonderful button that you will want to press when you have entered in all the stuff that you have to enter in. Simply really :)

    • Cancel : If you don't know what this will do, then I ain't gonna tell ya. Hehee

    Ok, now that we got that out of the way, we can actually start to do some cool editing here.

    This is what your screen should look like now. Ok, you got that? Now there are a few things that I will explain first. Here is a lit of what you will be playing with. Click on any of the entries to change their values. This image will explain what I am talking about here.

    • Bump Map Angle : This setting will determine what angle the Map will be "bumped" from. Not all that important really from what I have found. It seems to go very wierd if you set it to high levels (above 230) and also mkaes your phongs dissapear if it is set to a very low level.

    • Bump Map Light : This setting will determine just how much light there is on the Texture. It has got nothing to do with where lights will be placed in the actual game, just while you are making the texture. The more light there is, the lighter the water will be, and the less Phongs you will see (Phongs are explained later). I would suggest that you keep the Light at either the level that it starts off at, or add in a little more. Dark waters tend to look too black to be realistic, but everyone to their own eh? This setting bar will be brightest at 128. The scale will work in such a way that 0 is pretty much black water with little "phongs" of light (Yes, I will explain them in a sec) appear on the tops of the water, 128 is the brightest level possible with the water being almost white and the waves showing up nicely, and 255 will set the water to show waves once again, but there will be no Phongs Visible at all.

    • Drop Type : This will choose the type of "brush" you will be using to "paint" the waves onto the water. This is a drop menu, and there are heaps of options in it. I will list them all and what they do further down. Right now, all you need to know is that it chooses the type of drop/plop/gloop that you will be adding to the water.

    • FX Amplitude : Sadly, this feature does not seem to do anytihng right now. Perhaps it is a setting that will be implemented in the retial version of the editor.

    • FX Depth : I cannot find a real use for this entry, as it does not seem to change anything. I might be wrong, and if you find out what it doesn, then please tell me so that I might be able to explain it here, but until then, it gets a "Not Here" rating.

    • FX Frequency : This setting will change the speed at which the waves are produced from the actual wave brush. Ie, if you make an Oscillating Horizontal bar, and set it to 8 (The defualt) then you will get say, three waves every second. Set it to 32 and you will get 12 waves a second. This will not make the waves any larger, or higher, or make them cover a larger area, just will make more of the appear each second.

    • FX Phase : Well, here is another "I don't know if it does anyrthing" setting. It does not seem to make any real changes when you are playing with it, but Once again, if someone finds what it does (or knows) then please drop me a line, and I will correct myself here :)

    • FX Radius : Well, I would have thought that this setting would alter the radius that "random" water brushes (like rain drops) take the value of. Ie a larger number would make the rain fall into a larger area, but this does not seem to be the case. It doesn't seem to do much at all. Perhaps I am using the wrong bursh here, but I have tried it with MOSt of them, and it has done nothing. Oh well, I guess we are using a BETA editor eh?

    • FX Size : This one, I can tell you what it does :) When you change the setting here, it will change the size of the brush that you are using to "pain" the waves with. For example, if you choose a small setting (like 4) and use a vertical osillating brush, then you will make a waves that looks almost like a drop of water, but if you change the setting to say 32, then it will mkae a brush that is almost a quarter of the texture. This setting comes in handy when you want to make a wave texture that is not "repetitive" and so that you can make it look more natural and make the waves vary in size ond shape.

    • FX Speed : Ok, this one has got me beat. Sorry, No real idea what (if anything) it changes.

    • FX Time : Unfortunately this feature does not seem to be implemented here. I have once again tried it with a few different brushes, and it seems to make no difference to anything. Perhaps someone out there knows what it does, and want to mail me to tell me?

    • Phong Range : Well, I said that I would tell you what this means so here it comes. A Phong is the little bit of light that is reflected off a wave as it passes along near a light, or with a light behind it somewhere. When you have looked at the ocean or a lake, you will see that the water is full of little "glints" of light, and there is always a little bit of a sparkle somewhere. Well, this is called a Phong. The Phong Range setting basically determines how far away from the center of the brush, this particular phong can travel. If you play with just one oscillating brush, you will see what it really does. I would suggest that you set it to something small (like 4) and make a wave. You will see it right near the actual center of the brush but it will not travel all the way with the wave. If you set it to a higher number (like 128) then you will see that it should travel along with the wave to almost (if not all the way) to the end of the actual wave. Phongs will also be created when you have waves meet each other to form new waves.

    • Phong Size : This will determine just how large a phong is on a wave that is made. You can make the phong very small (like 4 or 8) and you will only see a small glimmer on the crest of the wave, or you can set it to a high number (255?) and you will be able to see that almost half the actual wave is covered in a bright light (ie the phong). I would suggest making it a fairly small number so that the glints look realistic, and not like they are all over the place, and I would also plan ahead and choose just how much light will be falling on that particular texture. If it is a small amount of light, then I would only use a few phongs here and there, but if you want to place the water right in the middle of a brightly lit up courtyard, then have a fair few of these things floating about, and it will enhance the effect of the lights, especially if you have water somewhere else that is a lot more "dull" so to speak.

    • Wave Amplitude : Well this one is easy :) This setting will determine just how large your waves are. The higher the number, the deeper and higher your waves will be. A low setting would be great to use if you want to make a water texture look as if it is almost calm, and a high setting would be good to use near a water fall. Play and see with this one, as it depends completely on what type of water you are trying to make.

    Ok, that pretty much covers all of the settings. here is a list of what each of the differnt drop types do. There are a lot of brush types to use, although most of them are actually just alterations of each other.

    • Drop Fixed Depth - This will make a little "dint" in the water that does not go up or down, but just stays at the level that you set it to. I find this one quite useless.

    • Drop Phase Spot - This will make a little drop in the water,that i just like having a drop of water falling into one part of thetexture all the time. It will also keep a nice little circular wave being formed around the drop center.

    • Drop Shallow Spot - This will be just like the "Drop Phase Spot" although it is much smaller.

    • Drop Half Ampl - This is once again similar to the "Drop Phase Spot" but it uses exactly half the Amplitude that you have set (Explained later). The size is very close to the "Drop Phase Spot"

    • Drop Random Mover - This will make a little pattern of waves dance all over your image. It follows no pattern, and actually looks quite like a little water bug running about on the surface of the water. Very cool and useful.

    • Drop Fixed Random Spot - With that name, you ight expact it to move about the texture, but it actually stay almost in the one place, only moving a little bit to the sides, so that it willmake the waves collide with each other, thus making them look much more realistic, then if it were just in the exact same place each time.

    • Drop Whirly Thing - Damn, I think that name is soooo creative.... ;) Anyways this setting will create a little "drop" of water that moves about in a small circle (not a regular one though :) and looks very much like a little bug once again that travels about on the water.

    • Drop Big Whirly - Same as above, but bigger andmakes bigger waves.

    • Drop Horizontal Line - Make a line that will go down into the texture. Ifyou place just one of these, it will most likely look like it dissapears soon, but if you place a LOT of them all over the texture, then you will see that it makes the texture look like it is all moving.

    • Drop Vertical, Diagonal 1 & 2 Line - Same as above, but the wave is in a different direction.

    • Drop Horizontal Osc - This will make a line wave (Like the above) but will actually keep the wave moving up and down all the time, so that it will akegreat looking waves on your texture. Very useful if you place even just a few on the surface, and let their waves meet and collide.

    • Drop Vertical Osc, Diagonal 1 & 2 Osc - Same as above, but in a different direction.

    • Drop Rain Drops - This will make a random drop fall in the general area of where you click the mouse. Very nice and useful.

    • Drop Area Clamp - This will "raise" a part of the texture and make it look like there was a pipe under it pushing water up into the surface of the water. I have found very little use for this one.

    • Drop Leaky Tap - This will make a drop fall into a set place (where you click) and it will keep dropping there in a steady and "leaky tap" like manner.

    • Drop Drippy Tap - Damn, who makes up the names eh??? Anyways, this is just like the one above, although the drops seem to come about twice as fast. Very useful.

    Right, that is out of the way. Now to actually paint with the stuff, all you have to do is to choose a wave brush (one of the things in the list above here) and click on the blue texture box. This is the box that has opened itself along with the properties bar. Nothing that hard there really is there? heh. Enjoy and make good water.


Zones

    Tomasz 'Millennium' Jachimczak
    tomasz@planetunreal.com

    Forward:

    I have had a few people ask me about the proper way to create zones so I best make a tutorial about it huh?

    Abstract:

    This will explain (in detail) how zones should be used, what they can do and how to set them up properly...

    Assumptions:

    I assume that you have a decent knowledge of the editor, and how to create a basic level.

    Tutorial:

    Introduction:

    Zones can be quite tricky if you don't really understand how they work, so if we explain them and how they work, life should be a WHOLE lot easier.

    Setting the scene:

    Zones are used by the editor to know when to stop filling an area with water, they also tell the editor when to make changes to the ambient lighting in an area. They can be used to accomplish a million things, but to do so we must understand how they work. A zone is simply an "invisible wall" that does not affect the player, ie doesn't stop them or anything like that, but tell the editor to stop doing whatever the last zoneinfo told them to do. A zone is automatically placed for example when you use the "Add Special > Waterzone" button. This not only adds in a sheet that can be textured, and made to look nice, and the rest of it, but places an invisible zone barrier, so when we place the "Waterzone Info" below the water line, the editor puts the water UNDER the shhet, then stops adding water to the rest of the room. You can set these barriers in pretty much any way you like, so if you want a complex waterfall, you can make it look really good by using a complex brush, then selecting "waterzone" and adding it in. A zone does not have to be a sheet! It can be any shape that can be made with the construction brush, although sticking to something that isn't too complex is normally a good idea...

    How to use zone barriers best:

    Technically you could set up a barrier wherever you like, but it is a good idea to be able to place them (assuming they are not a water zone ect) in a place where you will not readily be able to see both zones at the same time. I mean a good place for a barrier would be in a hallway, placed so that the player can see into either zone, but not both. This then stops any problems about Fog not being seen from afar or other problems that can occur. On this note, if you have fog that you would like to add in somewhere, then make sure that you set the barriers in a place where you will not be able to see the fog from. I mean, place the barriers around a corner for example, as when you set a zone to have fog in it, if you should be able to see the fog from afar, and the player is inside another zone, then they will see the light, but the fog will not be seen from the other zone EVEN if it should be in plain view - so keeping barriers around corners is a good rule of thumb to stick to...

    The best way to create the barriers:

    A barrier MUST completely fill the gap where you want it to be. This may sound silly, but most problems occur when a map editor places a barrier and it looks OK, but the editor keeps finding a TINY (It may not even seem to be there at all...) hole and assumes that it is not a barrier at all, so when you place a barrier, make sure that you create a sheet (or other shape) that is bigger then the area you want the barrier to be in, and INTERSECT it with the world. This means that the editor makes sure there are NO holes in it. This also is generally a better way, because it will make a smaller brush thus putting less load on the processor.

    Prefab zones:

    The editor comes with a LOT of pre-programmed zones, and for the most part, these will do quite nicely. There are "Water Zones", "Lava Zones" and a whole lot more. These should be allow you to create a fair amount. If you want to create a Fog Zone, then have a quick look at the "Fog" tutorial on this site, and it will tell you what you need to know. If you want to create your own settings for zones then I should have a tutorial posted on it shortly.


rogerbacon2

UT Skins Importing

by Shane Caudle

Importing Skins into UnrealEd Tutorial

In this tutorial I'll show you Step By Step how to import all the pcx's and bmp files that I released in a sample skin I did called OverKill. You can get it here.

Step 1

First you need to make sure you are in Texture mode under the browser.

Imp-Browse.jpg (18155 bytes)

Step 2

Now, at the bottom of the browser click on Import.

Imp-Import.jpg (16001 bytes)

Step 3

Now, select all the files you want to import. Just select all the Pcx's for now.

Imp-Files.jpg (57216 bytes)

Step 4

Now, name the package as Test and make sure Generate Mipmaps is turned ON, then click Ok to All.

(Use the name "Test" because there is a 15 character limit to the length of the Package name while importing it, you will re-name it later to SoldierSkins_OkerKill when you save it)

Imp-Files2.jpg (33192 bytes)

Step 5

Now at the bottom of the browser click on Import again.

Imp-Import2.jpg (16229 bytes)

Step 6

Now, select all the Bmp files you want to import.

Imp-Files3.jpg (50727 bytes)

Step 7

Now, leave the package name as Test and turn Generate Mipmaps OFF click Ok to All.

(The reason we want Mipmaps off is because we don't want the texture LOD to use Mipmaps on these chat faces because the are part of the HUD)

Imp-Files4.jpg (34107 bytes)

Step 8

Now, click Save .

Imp-Save.jpg (18367 bytes)

You will want to re-name the Package from Test to SoldierSkins_OverKill.

Imp-SaveAs1.jpg (53862 bytes)

Once you re-name it click Save.

Imp-SaveAs2.jpg (57004 bytes)

That's it, you now have a SoldierSkin_OverKill.utx in your UnrealTournament/Textures directory.

All you need to do now is make sure you have the SoldierSkins_OverKill.int file in your UnrealTournament/System directory and you now can use this skin.


rogerbacon2

SkiNs - Unreal Tournament
Creating new skin files

1 - DRAWING THE SKIN

- Download the template skins HERE:
The templates basically show a low contrast version of an existing skin - the black thunder skin for the 'male soldier' model. I've overlayed a skinmesh over the top so you can see the models wireframe.
(PRO) If you want to see the texture on the model as you create it, download THIS FILE (provided by Shane Caudle). Its a MAX scene of the soldier dude, with a single texture. This means you have to chop the texture up into 4 when you are done, but its worth it. This also means you can use 3d painters like meshpaint, painter3d, deep paint3d, etc if thats what you like.

- Rename the first 4 letters (xxxx) of all files to anything you want as long as its 4 letters. for this example i will rename them 'rogx'.
Also change the 'name' part of the last two pcx's to a suitable name for the face of your skin. I now have:

rogx1.pcx (The chest and arms)
rogx2.pcx (The Thighs and Boots)
rogx3.pcx (The shoulders and arms)
rogx4.pcx (The default face skin)
rogx4sixes.pcx (A face belonging to 'Sixes')
rogx5sixes.pcx (Sixes' portrait file for the chat messages)

(PRO) Note the files with 'sixes' in the name are additional face files. If i wanted to do another selectable face for my skin (i'll call him 'Bob') I would add 2 more files :- rogx4bob.pcx, and rogx5bob.pcx.

- Draw over them in the manner you see fit (hey, you're the artist!)
However know this - Each of the 5 pcx's has its own 256 color palette. You could have 256 shades of red on the arms and 256 shades of green on the legs if you so desire....
(PRO) make sure its Version 5 PCX or else you wont get 256 colours...

- Once you are sure they are top quality, start UNREALED

Step 2 : Importing the PCX's


rogerbacon3

SkiNs - Unreal Tournament
Creating new skin files

2 - IMPORTING THE TEXTURES

- Make sure 'Textures' is selected in the dropdownlist on the toprighthand side of the screen.

- click the 'new' button at the bottom to create a new texture package.

- enter anything as the name (I use 'deleteme') to create a temporary file, but call the the package what you want the skifile to be called. i called mine 666skins

- Click the Import button, then select all the pcx's of your new skin. They should now appear in the window on the right.

- Delete the (deleteme) file as you dont need it anymore

- Click Save to save the package.. use the same name as the one you used earlier. I called mine: 666skins.utx

- Close UNREALED as you dont need it anymore.

- This is a wierd bit. Now you have to rename the package to a one with 'soldierskins_' preceeding it. So in explorer I now rename my file to Soldierskins_666skins.utx

- Its not over yet.... Now you need to make a file that lets UT know where to get your new skin from, which is called an INT file.

back to Step 1:

Step 3 :Create the INT


rogerbacon4

SkiNs - Unreal Tournament
Creating new skin files

3 - REFERENCING YOUR NEW SKIN

- Create a file in your UnrealTournament \ SYSTEM directory using the same name as your package (ie Soldierskins_666skins.INT)

- Copy and paste the text below into your INT file. Then edit the names of the skins to match your package and skin names. When you have finished save and close the INT. Wasnt that part easy? ;)

[public]
Object=(Name=soldierskins_666skins.rogx1,Class=Texture,Description="Rogmeistah")
Object=(Name=soldierskins_666skins.rogx2,Class=Texture)
Object=(Name=soldierskins_666skins.rogx3,Class=Texture)
Object=(Name=soldierskins_666skins.rogx4,Class=Texture)
Object=(Name=soldierskins_666skins.rogx4Sixes,Class=Texture,Description="666")
Object=(Name=soldierskins_666skins.rogx5Sixes,Class=Texture)

- Now the UT server needs to be told to include the INT file you have just created. (else it wont show up in the player setup screen)
Open UnrealTournament.INI and look for the section that holds lines that look like these:

.........
ServerPackages=SoldierSkins
ServerPackages=CommandoSkins
ServerPackages=FCommandoSkins
ServerPackages=SGirlSkins
ServerPackages=BossSkins

Yours might look slightly differend depending on whether or not you have been installing new skins or mutators but this doesnt matter. Pop a line underneath that is exactly the same but references your new skin INT. Mine now looks like this:

...........
ServerPackages=SoldierSkins
ServerPackages=CommandoSkins
ServerPackages=FCommandoSkins
ServerPackages=SGirlSkins
ServerPackages=BossSkins
ServerPackages=Soldierskins_666Skins

AND THATS IT! Hopefully (fingers crossed), you should now be able to select your new skin in the Player setup screen. However, with this many steps to follow there is an equal amount of places to go totally wrong......
It took me a while to get a test skin working (I kept screwing up) so keep at it and follow the steps carefully.

GOOD LUCK!

Back to Step 2


Beginner's Guide to Using UnrealScript

Beginner's Guide to Unreal Using UnrealScript

Purpose

In this document I will attempt to provide an introduction to programming for Unreal using Unrealscript and outline the basic class structure that Unreal uses. This is intended for beginners who are looking for a starting point in creating Unreal modifications. I strongly suggest also reading Tim Sweeney's "Introduction to Unrealscript", (included in the reference section - eko) as this document is more specific in the application of Unrealscript to Unreal, and his introduction is more general to Unrealscript. This will not cover the use of header files and other libraries, as it is not currently available to the public at the time this document was written. Comments, suggestions, praise should all be sent to Ray Davis via email at ca@planetunreal.com". Thanks go to Tim Sweeney, Steven Polge, and the rest of the Epic team for making a cool game, and being cool developers who actually respond to questions. You guys rock. :-)

Introduction

Unrealscript is a language developed solely for developing game content for Unreal. It is based on Java and C++, so if you have any experience in either of those languages, or just C, the learning curve for Unrealscript will be relatively small, but if you do not have any previous experience I strongly suggest that you first learn one of those languages and then return to Unrealscript. Unrealscript adheres to a syntax style common to both Java and C++/C, sharing many keywords found in Java.

Unrealscript is an Object-Oriented-Programming language (OOP for short), which means that it is based on the concept of classes and inheritance. It is important to note that Unrealscript doesn't have all the features of a more complete OOP language such as Java, mainly it doesn't support things like multiple-inheritance, but luckily none of these missing features are necessary to make a fully functioning modification. There are several tutorials that will quickly get you acquainted with the concept of OOP if you are unfamiliar with it, Brandon Reinhart's being one that can be found here.

A unique feature to Unrealscript that is not found in either Java or C++ is states. States are a feature that allows you to define different implementations of functions within the same class (very similiar to function overloading in OOP), and generally they are used for time-based programming. For example, say you had a Player class that contained 3 states, Walking, Running, and Idle. Say that you wanted the Player to do a flip in the air if he jumps while running, and that you didn't want to have him jump at all while standing still (or Idling). One way to do this using states is to define your jump function in both the Running and Idle states, then in the Running state code a simple flip in that version of the Jump function, and then code a standstill in the Idle state's version of the Jump function. So now when your player is running (and thus in the Running state) it will do a flip if it jumps, and will do nothing if its not running. Sorry if this isn't a very good example, but it should help make it a bit clearer how states can be very useful.

Unreal stores Unrealscript code into packages (optionally along with textures, sounds, and models) in your Unreal\System directory. To keep all your new classes in a single package for easy distribution, just specify the same package name in the package field when you create your new subclasses.

Using Unrealed to edit UnrealScript

Unrealed has a builtin class browser, editor, and compiler for Unrealscript which is easily accessible. Start up Unrealed normally and on your right there should be a collapsable tree starting with "+Actor" and having several other classes underneath it such as "-Brush" and "-Info". By clicking on the "-" or "+" next to a classname you can collapse/expand the class tree to show its child classes, and by double-clicking on the class name itself it will bring up an editable window with the class code (if there is any) inside it. By right-clicking on the class name you can bring up a small menu that allows you to create a subclass of the current class or edit the default properties. NOTE: For most modifications you will want to subclass pre-existing classes and save them to your own packages, because if you modify the original Unreal code you will not be able to play with other versions of Unreal due to mismatches (mainly as a cheat preventing measure I believe).

To compile and save newly edited code just press F7 to compile all changed scripts, and then at the bottom of the tree select save, find you package, and click on save. Unrealed will then save you code (compiled or not) into a file in your Unreal\System directory by the name of your packagenamge and a ".u" extension (i.e. "blah.u" for the blah package).

Unreal's Class Structure

Object and Actor

Everything that is coded Unrealscript is derived from a very abstract and basic Object class, and from that we get many internal classes that deal with the engine itself (mainly sound, graphics, etc) and more importantly the Actor class which pretty much encompasses all of what you will want to modify for partial modifications. The Actor class defines the majority of standard variables and functions that are necessary for all classes, and all game-content related material is contained in classes derived from Actor. Players, weapons, artificial-intelligence, and even the game itself is derived from the base class of Actor. Important functions to note in Actor are BeginPlay(), Spawn(), Touch(), Tick(), and Destroy(). The BeginPlay() function is called whenever an instance of this class is put into play, into the game. It is very useful to initialize important variables and do other actions at the point of creation. The Spawn() function is used to create an instance of a class and put it into play. Touch() is called whenever this actor is touched by another actor, and is useful for causing damage or altering variables in the other actor. The Tick() function is called every game tick, generally as much as possible, so use it to perform actions continously or check for certain conditions. NOTE: Be careful not to put too much code into a Tick() function because it is very easy to overload the game and cause noticeable lag if too much is going on. Use the Destroy() function to destroy this instance of the class when you no longer want it to be in play.

Pawns

The Pawn class contains the ScriptedPawn (AI), FlockPawn (Insect/Flock AI), PlayerPawn (Players), and more recently the Bot classes, essentially all of the "living entities" in a level. The Pawn class defines basic variables, movement and other miscellanous functions common to all Pawns. Health, Groundspeed/Airspeed, and JumpZ are just some of the useful variables found in all Pawns.

PlayerPawn encompasses all players, a.k.a. clients, that are available in the game. It has several child classes, mainly UnrealIPlayer, then Human/Skaarj, and then the various male/female players. PlayerPawn contains all of the functionality of the players (movement, input handling) while the rest of the children classes handle all of the animations and sounds specific to the various players. The PlayerInput() and PlayerCalcView() are two functions that can be altered in your own classes to create some very cool modifications. PlayerInput() handles all input from the player, calls pertinent functions, and modifies appropriate data. PlayerCalcView() handles the calculations of the view of the player, and can easily modified to create a third person view, etc.

The ScriptedPawn is where you will find all AI-related game content (the exception being FlockPawns). This class defines the framework which Unreal's Skaarj, Nali's, Queen, and every other creature you will find in the game. The AI uses states heavily, and is based upon the common pathnode-using technique for navigation. Thankfully Steven Polge is a very good programmer, and has designed a very robust AI that can be used to make new creatures very easily with very little to no new code, using editable variables to define all aspects of the AI. I strongly suggest you read Steven Polge's "Unreal Creature Care and Feeding Guide" (included in the reference section - eko)if you plan to do any major AI programming, or if you just want to get a more in-depth understanding of how things work.

FlockPawns are special case AI that powers those wonderful schools of fish and swarms of flies that don't really do much other than look cool. You can modify these to make them a bit more intelligent if you wanted to I suppose, but they are pretty good at being insects already. NOTE: FlockMasterPawns actually handle the creation of FlockPawns, and you should spawn these instead of individual FlockPawns, as they are coded to self destruct if they don't have a FlockMasterPawn.

The Bot class used to be found beneath the ScriptedPawn class, but has since been moved to just a child of Pawn as its AI is noticeably different than average ScriptedPawns in its function and purpose. This is the class to study if you think you've got ideas on how to make more realistic/deadly/unique Bots in Unreal. NOTE: Bots still share the same initial structure as ScriptedPawns, so Steven Polge's AI guide will also be useful for understanding Bots.

Info

The Info class is a generalized class that encompasses all classes that deal with various game-related information, such as the current rules (Deathmatch vs SinglePlayer vs TeamGame), ZoneInfo which is used to create lava, water, or just plain empty space, and PlayerReplicationInfo which contains special networking information for Players. Most of my coding experiences in the Info classes has been in GameInfo to define new game types, or variations on the classics, such as ASMD-only deathmatch. It is also conceivable to subclass the ZoneInfo class to create new types of zones such as toxic goop from Mars, or maybe a specialized case of water that reflects weapon shots, etc.

Triggers

The Trigger class handles the operation of lifts, doors, and other events by responding to certain actions in the game and then performing another action, either by modifying another actor or by setting off other triggers, or both. Triggers are invisible in the game and are generally triggered by touch, proximity, or by another trigger. It is also to call the Trigger() function from another class to set off a trigger, say to open all the doors in a level at once or something. Subclass the Trigger class to create special case triggers, or other event-driven gameplay.

Effects

Effects covers all the special effects in Unreal, like explosions, laser blasts, etc. Subclass effects to create your own special effects, or combine other existing ones.

Inventory

The Inventory class, a child class of Actor, contains both the Pickup classes and Weapon classes. Pickup is all the items other than weapons that a Pawn will pick up and use in a game, be it health, armor, or ammo. Use the Pickup class to make your own unique items in a game, such as can of beans, or maybe even a landmine. The Weapon class handles all the functions for firing and aquiring weapons in a game. All of the individual Unreal weapons are defined as subclasses of the Weapon class, and handle the specifics of the weapons, such as animations and creating the projectiles/weapon effects. Just subclass weapon to create new weapons, provided you have a new model, or you can subclass one of Unreal's weapons and just use its model. NOTE: Weapons are based on an important variable, bInstantHit. If this is set to true, then the weapon will not create a projectile (such as a rocket), and instead will do instant damage to any targets in the line of sight (such as the rifle).

All the rest

There are many classes that are used to create the remaining things you see in Unreal, such as the HUD for players, lights, moving brushes, menus, or new projectiles. Experimentation, and just plain browsing through the code should help you gain a basic understanding of how everything else is layed out and how it works.

Vectors and Angles

Unreal uses the concept of Vectors and Angles to handle all of the movement and rotation of Actors in the 3d game world. A vector is a structure that consists of 3 components, X, Y, and Z values, where the X axis is forward/back, Y is left/right, and Z is up/down. You can modify these values directly to alter an Actors postition in the world, using the SetLocation() function with a new vector for the new location. Rotation is handled by an Angle structure which also consists of 3 components, Pitch, Yaw, and Roll, where Pitch will rotate up/down, Yaw to the left/right, and Roll will roll as though your were tilting your head to the left/right. Using SetRotation() you can alter what direction an Actor is facing, or convert a rotation to a vector to alter the direction of an Actor's velocity, etc.

If you're in college and would like to know more about 3d mathematics, I'd suggest taking some Physics classes and Linear Algebra, as vectors/matrices are common topics. Also try searching online for 3d mathematics tutorials, I know QDevels has a 3d tutorial, as does Weapons of Destruction on PlanetUnreal.

Conclusion

Although I'm sure you probably have many more questions, that is beyond the scope and purpose of this document, but you can find tutorials on more specific applications of Unrealscript at various websites, including Chimeric at http://www.planetunreal.com/chimeric. Also, make sure you frequent the Unreal Technology Page at http://unreal.epicgames.com, as it contains the latest news from the Unreal development team, and contains a lot of useful information.


Mod Authoring for Unreal Tournament

Brandon Reinhart
Epic Games, Inc.
brandon@epicgames.com
http://unreal.epicgames.com/

Last Updated: 12/08/99

Contents

  • Introduction
  • The Lazy Mans Guide
  • Some Advice to Start With
  • The Three Mod Types
  • A Few Things to Watch Out For
  • Getting Dirty: Setting Up Your Project
  • How to Build Your Package
  • How to Make a Mutator
  • The Anatomy of Mutator
  • Introduction to GameTypes
  • A first look at GameInfo
  • DeathMatchPlus, a specific game type
  • Adding a HUD
  • Adding a Scoreboard

Introduction

"Winds and storms, embrace us now,
Lay waste the light of day,
Open gates to darker lands
We spread our wings and fly away."- Emperor, Alsvartr (The Oath)

Writing 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 to show the world what he is capable of. This document is intended to give interested mod authors the information they need to write a successful mod for the Unreal engine. I 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.

The Lazy Man's Guide

So you want to get into UnrealScript hacking NOW NOW NOW. This is what you should do:

1. Export UnrealScript source files. Open UnrealED, set the Browser bar to Classes. Hit Export All. This dumps the UnrealScript source files to disk instantly creating your own SDK.

2. Create a package directory and hack some code. Like this:

    mkdir MyPackage
    cd MyPackage
    mkdir Classes
    cd Classes
    edit MyScriptFile.uc

3. Add your package to the EditPackages list in UnrealTournament.ini

4. Build your files with ucc. First delete your "MyPackage.u" if it exists. ucc make only rebuilds missing code packages. Then type "ucc make" from the System directory of your Unreal Tournamnt install.

Some Advice to Start With

When you begin writing 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. 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 3 while your work on the others. Pace yourself and think about the long term.

Everyone is an idea man. 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.

Now that you have your idea, you need to choose what kind of Unreal Tournament mod type is right for you. There are basically three types of mods. We'll go over each one in brief and then focus on them all individually.

The Three Mod Types

Mutators

Mutators are mini-mods. They have limited functionality as defined by the Mutator class. Mutators should follow certain rules. If you can't follow these rules, you should probably work on a GameType mod.

The first rule is that Mutators should be able to work with any other Mutator. 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 the "ModifyPlayer" 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.

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 Assault (another GameType).

GameTypes are implemented as subclasses of the "TournamentGameInfo" class. 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).

Everything Else

It is possible to write a mod that doesn't change gameplay through the GameInfo or Mutator classes. These would include Player Plugin Models (PPM) or a new weapon. We'll talk about a few prime examples like Weapons and Pickups later. GameTypes will often include many new weapons, pickups, AI features, or special actors that are separate from the game rules themselves.

A Few Things to Watch Out For

This is where I'm going to put all the information that I wish someone had told me when I started writing mods for Unreal. A lot of this information may not be relevant to you until you have more experience with the engine. I spent a lot of time out on my front porch with a buddy (Sel Tremble) talking about things like replication trying to figure out exactly how it all worked. That was definitely one of the most satisfying things I have ever done. Cracking open a new game can be a very cool experience, but also a very frustrating one. Here I'll give you a couple pointers to ease your exploration.

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. I'll briefly explain them to people who aren't so familiar.

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. 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. Let's look at some sample code:

class MyObject expands Info;

var PlayerReplicationInfo PlayerInfo;

function PlayerReplicationInfo TestFunction()
{
    return PlayerInfo;
}

Here we have a simple object called "MyObject" that is a subclass of Info. It has two properties: a variable called PlayerInfo and a function called TestFunction. You might want to interact with this object from inside your mod. Let's say you have a reference to a MyObject 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 MyObject Object1;
    local string PlayerName;

    Object1 = GetMyObject();
    PlayerName = Object1.PlayerInfo.PlayerName;
    Log("The player's name is"@PlayerName);
}

In this example we call a function called GetMyObject() to get a reference to a MyObject. We then access that reference to resolve PlayerInfo ("Object1.PlayerInfo") and then access the PlayerInfo reference to resolve PlayerName ("PlayerInfo.PlayerName"). But what if there isn't a MyObject available, or a bug in GetMyObject() causes it to fail to return a MyObject? 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 Object1 is assigned None. In the next line, we try and access Object1 to resolve the PlayerInfo reference. Uh oh....Object1 is None...it doesn't refer to anything. We can't access it, so the Unreal engine logs a warning saying the code broke: Accessed None in MyMod.GetPlayerName!

Its very easy to avoid buggy code like this. Just add a couple checks to your code and define special behavior in the case of a mistake:

class MyMod expands TournamentGameInfo;

function string GetPlayerName()
{
    local MyObject Object1;
    local string PlayerName;

    Object1 = GetMyObject();
    if ((Object1 != None) && (Object1.PlayerInfo != None))
        PlayerName = Object1.PlayerInfo.PlayerName;
    else
        PlayerName = "Unknown";
    Log("The player's name is"@PlayerName);
}

Now we are checking to see if Object1 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 Object1 is None, the code will never evaluate the (Object1.PlayerInfo != None) statement. 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).

Dangerous Iterators

UnrealScript implements a very useful programming tool called Iterators. An iterator is a datatype that encapsulates a list. (UnrealScript only supports list iterators, our next language will 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."

Sounds pretty cool, but lets think about it. 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. Never perform an AllActors iteration inside of Tick() and never perform AllActors iterations inside of other loops. (Okay, so saying NEVER is a little strict. Let's say...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, we've added a PRI array to GameReplicationInfo. 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.

The Foibles of Tracing

Wahaha. I just wanted to use the word foible.

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.

My 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 of your buddies, 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. Always think about the performance implications of code you write.

Decrypting Replication

Understanding replication is one of the most difficult aspects of writing a mod, but its utterly necessary if you plan on doing any netplay at all. Unfortunately, Tim's replication docs are not easy to understand and make some assumptions about the reader's knowledge that you may not possess. I'll try to point out the things that I learned only through trial and error.

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. Inside the level reference is a reference to the current game. You might write code that looks like this:

simulated function bool CheckTeamGame()
{
    return Level.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. What's wrong with this picture?

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. Yuck!

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. Lets look at it closely.

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++ 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 my C++. 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.

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 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).

Okay, so that should help you get into replication....let's move on.

Don't use UnrealEd

UnrealEd is a great editor for developing levels, but probably not the best place to work on code. This is a judgment call. I use Microsoft Developer Studio as my editor and "ucc make" to compile the package files. I find the Find In Files option in Dev Studio to be very useful and the editor to be very powerful.

In addition, UnrealEd hides the default properties blocks of source files, making them only accessible through the Show Defaults option. This just sucks! To export the script files to disk, go to the script browser and hit the "Export All" button. The files will be exported to their package directories ready for you to browse.

If UnrealEd crashes with a DLL or OCX error of some sort, go to unreal.epicgames.com and click on Downloads. Download the latest Unreal Editor Fix. The current fix level is 4.

Getting Dirty: Setting Up Your Project.

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 anything, textures, sounds, music, or compiled game code. 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 have different suffixes (.utx, .uax, etc) they are still the same kind of file.

You are going to be dealing with .u files, or code packages. Code packages primarily contain compiled UnrealScript, but may also contain textures and sounds that the code depends on.

UnrealScript is an object oriented language. If you aren't familiar with OO, now is a good time to take a detour and read my guide to object oriented programming. Here is the link: http://www.orangesmoothie.org/tuts/GM-OOtutorial.html. This document is fairly old, but still a good resource.

Since UnrealScript (lets call it US for short) is object oriented, you won't be editing any of the original source. This is different from Quake, where you edit the original source and then distribute a new DLL. In US, you will subclass the classes that shipped with Unreal Tournament, modifying them to suit your needs.

So lets make a package. I'm going to refer to our test package as "MyPackage" but you will want to call it the name of your mod. Where I say "MyPackage" you'll want to replace with your own package name. Make a directory in your Unreal Tournament directory called MyPackage. Underneath this directory, make a directory called Classes. The UnrealScript compiler looks in the Classes directory for source files to build.

Now, edit UnrealTournament.ini and search for EditPackages=. You'll see a list of all the Unreal Tournament packages. Add your package to the list:
EditPackages=Core
EditPackages=Engine
EditPackages=Editor
EditPackages=UWindow
EditPackages=Fire
EditPackages=IpDrv
EditPackages=UWeb
EditPackages=UBrowser
EditPackages=UnrealShare
EditPackages=UnrealI
EditPackages=UMenu
EditPackages=IpServer
EditPackages=Botpack
EditPackages=UTServerAdmin
EditPackages=UTMenu
EditPackages=UTBrowser
EditPackages=MyPackage

Lets take a break and figure out what all those packages are for!

Core contains fundamental unrealscript classes. You won't need to look at the stuff in here much at all. Notice that Core, like many .u files, has a related DLL. The DLL contains the C++ part of the package.

Engine is where things get interesting. You'll soon become very familiar with the classes in engine. It contains the core definitions of many classes that will be central to your mod. GameInfo describes basic game rules. PlayerPawn describes basic player behavior. Actor describes the basic behavior of UnrealScript objects.

Editor contains classes relevant to the editor. You'll never need to mess with this, unless you become a totally elite hacker.

UWindow contains the basic classes relevant to the Unreal Tournament windowing system. This is a good place to research how the system works if you want to add complex windows and menus to your mod.

Fire contains the UnrealScript interface to the "Fire Engine." The fire engine is the code that makes all the cool water and fire effects in UT.

IpDrv contains classes for putting a UDP or TCP interface into your mod. We use this for the IRC interface in the game, among other things.

UWeb contains classes for remote web administration.

UBrowser contains the core classes for the in game server browser.

UnrealShare contains all the code from the shareware version of Unreal. Nalis galore!

UnrealI contains all the code from the full version of Unreal. UnrealShare and UnrealI are included in UT because some UT code is based on classes in these packages. There is a LOT of content here you could use for your mod.

UMenu contains any menus for UT that don't depend on Botpack.

IpServer contains the GameSpy querying interface.

Botpack the soul of the new machine. Contains all of the game logic for Unreal Tournament. Tons of kick ass toys to play with. This is where a lot of your research time will be spent.

UTServerAdmin contains Unreal Tournamnt specific web admin code.

UTMenu contains UT menus that require content from Botpack.

UTBrowser contains browser code that requires content from Botpack.

Notice that the order matters here. This is the order in which the compiler will load the packages. "TournamentGameInfo" in Botpack is a GameInfo, so in order for the compiler to build that code, it needs to have Engine loaded. Your mod should go at the end of the list to benefit from all the code in the previous packages.

How You Build Your Package

Now that your package is set up, you are ready to build it. You don't have any code written yet, so lets make a very simple useless mod. This will serve as a good example of making a new GameType.

In the MyPackage/Classes directory, create a file called MyGame.uc. Put the following code inside of it:

class MyGame expands DeathMatchPlus;

defaultproperties
{
    GameName="My Game"
}

This creates a class called "MyGame" that is a subclass of DeathMatchPlus. All we are doing is changing the name of the game...pretty simple. Notice the inheritance in action. All the code that makes up DeathMatchPlus is now a part of your game type.

Save the file and change directory to System. Type 'ucc make' and watch the code burn. Pretty soon, your package will be compiled and a new MyPackage.u will be sitting in the System directory.

In order to make our new gametype show up in the menus, we have to give it a metaclass definition. Create a file in the System directory called MyPackage.int. INT files primarily contain localization information for foreign versions of the game, but they also contain extra information about the classes inside a package.

Add the following lines to your int file:

[Public]
Object=(Name=MyPackage.MyGame,Class=Class,MetaClass=Botpack.TournamentGameInfo)

Save it and exit. When UT's menus search for games to list in the Game selection window, they search all the .int files in the System directory for classes that have a MetaClass of Botapck.TournamentGameInfo. Now your game will show up on that list. Start Unreal Tournament and go to Start Practice Session. Find your game on the list. If you start it, "My Game" will show up as the game's name in the scoreboard. Cool!

So that's how you build a basic project. Obviously writing a mod is a lot more complicated than that. Now we'll get down and REALLY dirty by looking at the specifics of different types of mod.

Making 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 said above, 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).

All mutators descend from the Mutator base class either directly or indirectly. Let's make a Vampire mutator and see how it all works. Create a new file in your package classes directory called Vampire.uc. Drop the following code in there:

class Vampire expands Mutator;

var bool Initialized;

function PostBeginPlay()
{
    if (Initialized)
        return;
    Initialized = True;

    Level.Game.RegisterDamageMutator( Self );
}

function MutatorTakeDamage( out int ActualDamage, Pawn Victim, Pawn InstigatedBy, out Vector HitLocation,
    out Vector Momentum, name DamageType)
{
    if (InstigatedBy.IsA('Bot') || InstigatedBy.IsA('PlayerPawn'))
    {
        InstigatedBy.Health += ActualDamage;
        if (InstigatedBy.Health > 199)
            InstigatedBy.Health = 199;
    }
    if ( NextDamageMutator != None )
        NextDamageMutator.MutatorTakeDamage( ActualDamage, Victim, InstigatedBy, HitLocation, Momentum, DamageType );
}

The first line declares the class contained in this file. US is like Java in that each file contains a separate class definition. We are saying that our class, Vampire, is a Mutator. It "inherits" all the properties of Mutator.

Next, we declare a class member. In UnrealScript, all class member variables must be declared before any functions (also called methods) are declared. The var keyword tells the compiler what we are doing. Here we have a Boolean (true/false) value called Intialized.

Next we have a function called PostBeginPlay. To someone who isn't experienced with object oriented programming, we have a bit of a puzzle. This object just declares functions, it doesn't seem to have any entry point! The "entry point" is inherited. Vampire is a Mutator, so it does everything Mutators can. Mutator is an Info, Info is an Actor, and an Actor is managed by the engine. In our case, we are overriding the function PostBeginPlay. PreBeginPlay, BeginPlay, and PostBeginPlay are called on every Actor in the level when the game starts. They are used to initialize the world.

Our PostBeginPlay function starts by setting initialized to true. Notice it'll return if initialized is already true. What's the point of that? Well, the problem is that the BeginPlay suite of functions get called twice on Mutators. Unfortunately, this is somewhat unavoidable. BeginPlay and its friends are called on Actors when they are spawned and when the game starts. Mutators, unlike other actors, are spawned before the game starts...so you get two calls. The Initialized check is to prevent the rest of PostBeginPlay from being executed twice.

The second part of PostBeginPlay is called RegisterDamageMutator, a method located in the GameInfo class. Here we are accessing the "Level" property that all Actors inherit. Then we access the "Game" property of the LevelInfo class that Level points to. Finally, we call the function from that reference, passing our "Self" as the parameter.

RegisterDamageMutator is a special method that tells the GameInfo to call MutatorTakeDamage on this mutator whenever a pawn takes damage. Because pawns take a lot of damage during the course of a normal game, we don't want to call this function on every mutator, that would be slow. RegisterDamageMutator allows us to limit the calls to only a subset of mutators. (By the way, in UT pawns consist primarily of Bots and PlayerPawns).

Next we have our implementation of MutatorTakeDamage. This is the heart of our Mutator. We are making a Vampire mutator. The idea is simple: if a Pawn A does damage to another Pawn B, give the Pawn A health equal to the amount of damage. Players and bots will effectively suck life off of other players or bots.

As I mentioned above, RegisterDamageMutator is called on our mutator whenever a player takes damage from another one. The pawns in question are passed to us in the variables.

We start by making sure we are only dealing with bots and playerpawns. There are pawns that are neither bots nor players and we don't want to deal with them. InstigatedBy refers to the player who dealt the damage. We do our core logic by adding to that pawn's health life equal to the damage dealt. RegisterDamageMutator always passes in an amount of damage AFTER armor, so this is raw final damage. Finally, we don't want a player gaining so much life he becomes unkillable, so we limit the total life gain to 199 points.

To finish the function off, we call RegisterDamageMutator on the next damage mutator in the list. It is critical that you pass along method calls like this. If you fail to, damage mutators loaded after your own won't work right. There are other functions that need to be passed along, which we'll look at below.

So now you can save this file and rebuild your package. We aren't done yet, though, because the mutator won't show up in the menus without some more work. Open your package's int file and add the following line to the [Public] section:

Object=(Name=SemperFi.Vampire,Class=Class,MetaClass=Engine.Mutator,Description="Vampire,You gain life equal to the amount of damage you do to an enemy.")

When the game looks for mutators to list in the Add Mutators window, it searches all .int files for objects with a declared MetaClass of Engine.Mutator. We've also added a Description for the help bar. Now we are ready to run Unreal Tournament and load the mutator. Play around with it for a bit and you'll probably get ideas for your own mutators or ways of expanding Vampire.

The Anatomy of Mutator

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. Let's take a close 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.

We'll skip the PostRender function for now and look at ModifyPlayer. This 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.

ScoreKill is called whenever a pawn kills another pawn. This lets you influence the score rules of the game, preventing point gains in certain situations or awarding more points in others. Remember to call Super.ScoreKill() if you override this function.

MutatedDefaultWeapon gives you an opportunity to give a different default weapon to a player that enters a game or respawns. In UT, the default weapon is the Enforcer. If you just want to change the default weapon, you don't need to override this function. Instead, just add a DefaultWeapon definition to the defaultproperties of your mutator. (See the bottom of PulseArena for an example).

You don't need to mess with MyDefaultWeapon or AddMutator.

ReplaceWith and AlwaysKeep allow 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 Botpack.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.

Mutate is cool. It 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.

MutatorTakeDamage, as described above, is called on DamageMutators. It lets damage mutators do some kind of game logic based on a pawn taking damage. It also tells you where the pawn was hit, the type of damage, and how much force the damage imparted.

RegisterHUDMutator is used to tell the game that this mutator should get PostRender calls. PostRender passes you a Canvas every frame. You can use the Canvas to draw stuff on the HUD. Look in the Botpack.ChallengeHUD class for extensive examples of abusing the Canvas. Hehe.

The best way to learn mutators is to read the code in the mutators that ship with UT. In fact, you'll probably want to spend a lot of time just pooring over the massive amount of code that comes with 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 either or read other mod authors code. If you spend the time it takes to learn, you will be rewarded with the ability to take on larger, more difficult projects.

Introduction to GameTypes

Where to start, where to start? This is the meat. The big bone. Now we start getting into the hard stuff. Mutators can do some cool stuff. They are pretty easy to understand and they can do a lot of things by interacting with the game. 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 (say a Jailbreak 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 Botpack package. TournamentGameInfo contains code that is universal to all of Unreal Tournament's game types. DeathMatchPlus contains the code for running a normal death match. TeamGamePlus contains code for team deathmatch as well as general team management code. Domination and Assault, which are subclasses of TeamGamePlus, implement those particular game types.

The first step in writing your new game type is to determine which class to subclass. If you are writing a team game, you'll want to subclass TeamGamePlus. If you are writing a game without teams, use DeathMatchPlus. If you are writing a game that departs significantly from any previously styled game type, use TournamentGameInfo. Subclassing is very beneficial...you immediately inherit all of the code in your parent classes.

Lets look at a very simple Game Type mod:

class MyGame expands DeathMatchPlus;
defaultproperties
{
   GameName="My Game"
}

The above code, when saved in a file called "MyGame.uc" will build a new game type. The only difference here is that we've changed the name to "My Game." 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 DeathMatchPlus...we haven't actually added any new behavior.

Like Mutators, we need to do a little INT file hacking in order to get the new game type to show up in the menus. Edit your package's INT file and add the following lines to the [Public] section:

Object=(Name=MyPackage.MyGame,Class=Class,MetaClass=Botpack.TournamentGameInfo)
Preferences=(Caption="My Game",Parent="Game Types",Class=MyPackage.MyGame,Immediate=True)

The practice session and start server menus look in all INT files for declared objects that have a MetaClass of Botpack.TournamentGameInfo. If you add these line to your package's INT file, you'll get an entry called "My Game" in the list of games. The name is taken from the GameName variable of your GameInfo class. The Preferences line gives your game a configuration entry in the Advanced Options menu. You probably don't need to worry about that right now.

So now we have a simple game to start messing with. What do we do? Well lets look at a few of the methods available in GameInfo. Remember, you'll need to do a lot of research on your own. You'll only become a strong UnrealScript hacker if you spend time to acquaint yourself with the code at your fingertips.

A First Look at GameInfo

Open the Engine package file "GameInfo.uc." This 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 the Timer function. In GameInfo its pretty short, but in DeathMatchPlus 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 TournamentGameInfo 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. DeathMatchPlus 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.

Scroll down in GameInfo until you find 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. A little ways below 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 UnrealTournment's DeathMatchPlus, the player is given an ImpactHammer and an Enforcer. LastManStanding has a great example of doing cool things with AddDefaultInventory. It gives the player every weapon in the game (except for the Redeemer) as well as some armor and a lot of ammo. 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 map designer adds to their map is one such location. In TeamGamePlus, 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 Terrorists in one location and Snipers 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. It also logs the event to the ngStats log. Finally, it calls ScoreKill.

ScoreKill awards points for a kill. DeathMatchPlus assigns a frag for a successful kill and subtracts one for a suicide. TeamGamePlus 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 thats a quick look at the more important GameInfo functions. The advanced GameInfo classes like DeathMatchPlus add important new behavior for controlling bots and single player games, as well as refining the GameInfo methods into specific rules. We'll look at DeathMatchPlus next.

DeathMatchPlus, a specific game type.

Adding a Heads Up Display

Notice the GameInfo variable HUDType. This is used to specify the type of HUD the player will be given if they play your game. DeathMatchPlus uses a HUDType of Botpack.ChallengeHUD. The ChallengeHUD class is the primary HUD for Unreal Tournament. Let's take a look at adding custom HUD elements.

First, create a subclass of ChallengeHUD. Lets call it MyHUD:

class MyHUD extends ChallengeHUD;

Now add MyHUD to your gametype's HUDType. In the defaultproperties set MyHUD equal to class'MyPackage.MyHUD' Remember, you can't see defaultproperties if you are editing UnrealScript from UnrealED. Make sure you've exported the source classes and are editing using your own text editor like CoolEdit or MS Dev Studio.

A HUD does all of its drawing in the PostRender function. PostRender is called after the world has been drawn and all the models in the world have been drawn. The function passes you a canvas, which is an object that is used as an interface to the player's screen. Add a PostRender function to MyHUD:

function PostRender(canvas C)
{
    Super.PostRender(C);
}

What does the Super call do? It calls the parent class version of PostRender. MyHUD's parent class is ChallengeHUD, so that version of PostRender is called. If you add your custom code after the call to Super.PostRender, you'll be able to add elements to the HUD that will draw on top of all the other HUD elements. If you don't call Super.PostRender all of the basic HUD elements like weapon readouts and so forth will not be drawn.

The Canvas class is defined in the Engine package. You might want to open it up and get a look at its member functions. The ChallengeHUD class is full of good examples on how to draw stuff to the HUD. As an example, lets just draw the player's name on the HUD:

function PostRender(canvas C)
{
    Super.PostRender(C);
    C.SetPos( 0, Canvas.ClipY/2 );
    C.DrawColor.R = 255;
    C.DrawColor.G = 0;
    C.DrawColor.B = 0;
    C.Font = MyFonts.GetBigFont( C.ClipX );
    C.DrawText( PlayerOwner.PlayerReplicationInfo.PlayerName, False );
}

This code sets the canvas drawing position to halfway down the screen and all the way to the left. Next, it sets the drawing color to be a deep red. It then asks MyFonts (the ChallengeHUD font info object) to return an appropriate big font. The font size returned depends on the screen's resolution, so we have to tell the FontInfo class what the X length of the screen is. Finally, we draw the player's name.

Your HUD can be much more complex...adding scrolling features and new types of information readouts. You'll want to look over ChallengeHUD's PostRender function and see how it gets information about the world from PlayerOwner and other related objects. Skillfully changing the HUD can add a whole new look and feel to your modification.

Adding a Scoreboard

Scoreboards work just like HUDs. If you create a new scoreboard class that extends TournamentScoreboard and put the class in your gametype's ScoreBoardType variable, players will be given a scoreboard of that kind. A scoreboard draws its information whenever the Player's bShowScores variable is true. It draws through the PostRender function and is given a Canvas parameter. Look at TournamentScoreboard for an example of player sorting and score listing.


Banes Mutator Tutorial

Recently, I made a serious effort in trying to figure out how Unreal's Mutators work. Posts on the Unreal Techpages suggested that some interesting things could be accomplished by using this new, obscure class, yet no matter where I looked, I could find no guidelines of how to actually use them.

During a visit on the IRC, however, I stumbled across Phantal, who had experimented a bit with this new feature. The discussion put me on the right track, so I'd like to extend my thanks for his help.

First of all, let's make it clear that I will only go through the basic use of Mutators in this tutorial - mainly because I'm more or less starting out myself. However, I am aware of the general use of the class(es), so hopefully this doc will help people to get started.

To use a Mutator, you need to inform Unreal that it should add it to the game. The format, expressed as a hyperlink, is:

unreal://map.unr?mutator=mutatorpackage.mutatorclass?game=
gamepackage.gameclass

The various links used within this document are, of course, local ones. If you wish to use Mutators without having to create documents containing a lot of hyperlinks, you may launch "mutated Unreal sessions" (heh) from DOS by replacing "unreal://" with "unreal.exe ". Should you prefer to launch games from within Unreal, bring up the console line and replace the "unreal://" part with "open ".

Why Use Mutators?

Mutators, as the term might suggest, are pretty good at modifying and replacing stuff. Although this can be accomplished by creating a new game type, one of the main advantages of the Mutators is that you don't need a new game type. In fact, you can combine Mutators with any game type, including 3rd party mods.

Another reason to use Mutators is that during network play, the clients will not need to download anything to join, unless the Mutator makes use of actors not shipped with Unreal, of course. So, a player can connect to a server, find himself on a version of Ariza where all weapons have been replaced and/or modified, yet he did not need to download any mod or map to participate. Nifty!

Basic Use of Mutators

Mutators are not game types, nor are they in any way as flexible as game types. If you need to use a special scoring system, custom kill/death rules or whatever, you need to create a new game type. However, if you simply wish to control what items are to be spawned on a level, Mutators are an interesting option.

Mutators are basically replacement classes. They are launched whenever an actor is spawned and by overriding some of the classes found in the standard Mutator class, we can control what items are to be spawned and which ones to scrap. We could also include special checks, such as counters that would allow us to spawn only a specific amount of a certain item. In order to actually use a custom Mutator, we need to instruct the game to use it - this will be covered below.

To create a simple Mutator, we'd want to modify the function CheckReplacement() found in the base Mutator class. This function is called by a Mutator's IsRelevant(), so basically we'd use it to:

  • Check whether or not an item is to be kept or scrapped.
  • Execute replacement code for those items that are to be replaced.

Remember, IsRelevant() is called prior to an item being placed on the level. For those of you not familiar with this function and its GameInfo equivalent, I'll explain the basics, step-by-step:

  1. When a level is loaded and a game is about to start, the engine will attempt to spawn the various actors present on the map. These actors include just about anything - items, triggers, navigation points, pawns and so forth. However, when an actor is about to be spawned, the engine enters the GameInfo's IsRelevant() function to see whether or not it should really create it. In effect, the PreBeginPlay() of any actor includes a call to IsRelevant(), meaning that when IsRelevant() is executed, the actor is yet to appear on the level.
  2. The GameInfo's IsRelevant() immediately calls the current Mutator's IsRelevant() function.
  3. The Mutator's IsRelevant() has the following format: IsRelevant( Actor Other,out byte bSuperRelevant ). "Other" should be pretty obvious - this simply refers to the actor we're currently examining. "bSuperRelevant" is special, however. Although I haven't really used this one myself, my impression is that it is used to override any following replacement attempts - by setting bSuperRelevant to 1, the actor will be kept no matter what (this would include any replacement attempts made by the GameInfo's IsRelevant() as well). However, you need to explicitly support this function in your own Mutators if you wish to use this. Again, it's not something I've bothered using so far.
  4. .The Mutator's IsRelevant() calls another Mutator function - CheckReplacement(). This is where the actual condition checks and replacements take place. If this function removes an actor and/or replaces it with a new one, it should return False. If no changes have been made, it should return True. IsRelevant() will use this return value in step 5 below.
  5. If the Mutator's IsRelevant() returns True, the engine will run the rest of the code in the GameInfo's IsRelevant() function. If, however, it returns False, the function will be exited and the actor will be destroyed.

Creating a Simple Mutator

Confused? Well, it's not that hard, really. To clarify things a bit, have a look at the following, very basic example. To test it, you need to include the Mutator in the command line. Click on this link to try it out: unreal://dmariza.unr?mutator=ASMDMutator.ASMDMutator?game=
UnrealShare.DeathmatchGame.

Now for the actual code. It's intentionally been kept simple and will turn any Unreal map during any game type into an ASMD fragfest.

//===========================================================

// ASMDMutator.

//===========================================================

class ASMDMutator expands Mutator;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)

{

if ( Other.IsA('Weapon') && Other.class!=class'UnrealShare.ASMD' ) {

ReplaceWith(Other, "UnrealShare.ASMD");

return False;

} else if ( Other.IsA('Ammo') && Other.class!=class'UnrealShare.ASMDAmmo' ) {

ReplaceWith(Other, "UnrealShare.ASMDAmmo");

return False;

}

return true;

}

What we're doing here is first to check whether or not we're dealing with a weapon and if so replace it with the ASMD. Note that I've included a check to make sure that the weapon we're examining is not an ASMD. Why? Well, as I have previously mentioned, IsRelevant() and CheckReplacement() are executed prior to the item being placed on the level. If we hadn't included this check, we'd enter an infinite loop as the Mutator tries to replace the ASMD with another ASMD, then checks whether it should replace the replacement and realises that it's a weapon and then repeats the whole process.
After replacing a gun, we exit the function and return False to the caller (i.e. IsRelevant() ). This will cause the original item to self-destruct; had we returned True here, we'd end up with both the original item and the replacement, located on the same spot.
If, however, it's not a weapon we're dealing with, the code determines whether or not it's an ammo item. The principle is the same here - we make sure that it's not an ASMD ammo we're dealing with, then replace the original item and exit.

The code above will affect the default gun as well, but probably not in the manner you would expect. As the default gun no longer exists (we are replacing the Dispersion Pistol), you'll end up starting the match without any weapon at all. To fix this, we need to modify another Mutator function - MyDefaultWeapon(). As the Mutator we're creating will exchange all weapons on a level with ASMD's, the function we're going to write will simply return the ASMD's class to the engine, like this:

function Class<Weapon> MyDefaultWeapon()
{

return class'UnrealShare.ASMD';

}

As you can see, it's ridiculously easy to create simple Mutators whose only function is to replace the default gun..

Try it out: unreal://dmariza.unr?mutator=ASMDMutator.ASMDMutator2?game=
UnrealShare.DeathmatchGame

Adding Additional Mutators

One interesting aspect of Unreal's Mutators is that you can link them. By doing so, we can combine several small Mutators and get quite interesting (or disastrous :) results. Consider this small Mutator:

//============================================================
// LimitAmmoMutator.
//============================================================
class LimitAmmoMutator expands Mutator;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{

if ( Other.IsA('Weapon') && Weapon(Other).PickupAmmoCount==Weapon(Other).Default.PickupAmmoCount )

Weapon(Other).PickupAmmoCount=Max(Weapon(Other).PickupAmmoCount*0.5, 1);

else if ( Other.IsA('Ammo') && Ammo(Other).AmmoAmount==Ammo(Other).Default.AmmoAmount )

Ammo(Other).AmmoAmount=Max(Ammo(Other).AmmoAmount*0.5, 1);

return true;

}

This Mutator will halve the amount of ammo found in all guns and ammo pickups. As you can see, you're not limited to simply replacing stuff using Mutators - you can modify the properties as well.
Now, we could combine this new Mutator with our former one, by using something along these lines:

unreal://dmariza.unr?mutator=ASMDMutator.ASMDMutator2,LimitMutator.
LimitAmmoMutator?game=UnrealShare.DeathmatchGame

The resulting game would be a Deathmatch using ASMD's only, with only half the amount of ammo you'd normally expect available.

So, to sum it up - in order to launch several Mutators at once, you simply enter each Mutator in the command line, separated by a comma. Note however that you must not add a blank after the comma (like "Mutator1, Mutator2") - if you do, you're likely to get an error message.

The Akimbo Mutator

The following Mutator uses a 3rd party mod written by yours truly. So, in order to try it out, you need to have the "AkimboDM" mod installed on your system - get it from Weapons of Destruction (http://www.planetunreal.com/wod/). Now, basically this code is quite similar to the ASMD Mutator we've seen previously:

//=============================================================
// AkimboMutie.
//=============================================================
class AkimboMutie expands Mutator;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{

if ( Other.IsA('Weapon') && Other.class!=class'BaneAkimboMag' ) {

ReplaceWith(Other, "AkimboDM.BaneAkimboMag");
return False;

} else if ( Other.IsA('Ammo') ) {

if ( Other.class!=class'UnrealShare.ShellBox' && Other.class!=class'UnrealShare.Clip' ) {

if ( Ammo(Other).ParentAmmo!=None )

ReplaceWith(Other, "UnrealShare.Clip");

else

ReplaceWith(Other, "UnrealShare.ShellBox");

return False;

}

}
return true;

}

function Class<Weapon> MyDefaultWeapon()
{

return class'AkimboDM.BaneAkimboMag';

}

As you can see, the main difference here is the way ammo items are handled. In order to replace original ammo pickup in a proper way, we check whether or not the item has got a "parent". For instance, the parent of the RifleRound is the RifleAmmo class, so in this case we'd replace the RifleRound with a Clip and the RifleAmmo with a Shellbox.

In order to make ammo a bit more scarce, we might want to use the LimitAmmo Mutator. However, when picking up a Shellbox, the game will claim that we've acquired 50 bullets, even though we've actually only received 25. Let's expand the old LimitAmmo Mutator and change this:

//===========================================================
// LimitAmmoMutator2.
//===========================================================
class LimitAmmoMutator2 expands LimitAmmoMutator;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{

Super.CheckReplacement(Other, bSuperRelevant);
if ( Other.class==class'UnrealShare.ShellBox' && Ammo(Other).PickupMessage==Ammo(Other).Default.PickupMessage )

Ammo(Other).PickupMessage="You picked up 25 bullets";

return true;

}

As the Akimbo mags want access to a AkimboDM GameInfo setting called pTimeLimit, it's probably not a good idea to combine the Mutator with any other game type than the AkimboDM. While it would work, it would also generate a whole lot of error messages in the log file, so let's stick to the Akimbo game type. Try out the result by clicking on this (long) link:

unreal://dmcurse.unr?mutator=akimbomutie.akimbomutie,limitmutator.limitammo
mutator2?game=AkimboDM.Akimbo_DM

Note that as the AkimboMutie makes use of a 3rd party mod, we need to include the "AkimboDM" mod as a ServerPackage if we're going to use the Mutator on a server. Read more about how to accomplish this in "Using Mutators on Servers" below.

Generic Mutators

The Mutators we've had a look at so far are pretty specialised Mutators. They do their job, but their job is pretty restricted to one, single thing. You can, however, code much more generic Mutators, for instance by using Default Properties or *.ini files. This is probably preferable if you intend to write a "true" replacer, which will allow you to replace any item with any other item. For instance, we could create a generic "Weapon Mutator", which would read a list from the Mutator's ini (by using config variables) and then replace weapons based upon the info gathered from that list.
However, be careful when swapping slots between two guns. For instance, let's say that we decide to replace all Automags with Stingers and all Stingers with Automags. When the level is loaded, the Mutator gets to work. Let's assume that the first actor it finds is an Automag - the code would then try to replace it with a Stinger. However, as the new Stinger is spawned, the Mutator kicks in again. It finds that Stingers are to be replaced with Automags.. And so on. Make sure that you add condition checks to avoid errors such as this one.

What follows is what could be considered as an early version of a generic weapon Mutator. It's merely given as an example, so I've not included it as a *.u file.

//=============================================================
// BaneWeaponMutator.
//=============================================================
class BaneWeaponMutator expands Mutator config (BaneMutators);

var config string OriginalWeapon[10], ReplacementWeapon[10];
var bool bIgnore;

function bool CheckReplacement(Actor Other, out byte bSuperRelevant)
{

local int i;
local class<weapon> wpn;

for (i=0; i<10; i++) {

if (OriginalWeapon[i]!="") {

wpn=class<weapon>(DynamicLoadObject(OriginalWeapon[i], class'Class'));
if (Other.class==wpn) {

if (ReplacementWeapon[i]!="") {

bIgnore=True;
ReplaceWith(Other, ReplacementWeapon[i]);

}
return False;

}

}

}

return true;

}

function bool IsRelevant(Actor Other, out byte bSuperRelevant)
{

if (bIgnore) {

bIgnore=False;
return True;

}

return CheckReplacement(Other, bSuperRelevant);

}

To make this Mutator work, we obviously need two lists of weapons - one containing the original ones (i.e. the standard Unreal guns) and one containing the replacements. Guns that are not to be replaced should not even be included in the list - for instance, this is a perfectly acceptable ini:

[BaneMutators.BaneWeaponMutator]
OriginalWeapon[0]=UnrealShare.AutoMag
ReplacementWeapon[0]=UnrealShare.Eightball

The code above is a bit "dirty", as it uses something that could be considered a hack to avoid the Automag/Stinger problem I mentioned previously. A Boolean variable called bIgnore is set right before the original gun is replaced. A tweak to IsRelevant() causes the code to automatically accept a gun whenever the bIgnore flag is set. Thus, we'll not enter the annoying infinite loop I described. Not that pretty, perhaps, but it seems to work :)

Using Mutators on Servers

If you've made some Mutator that simply modifies standard Unreal actors and/or replaces them with other standard actors, running a "mutated server" shouldn't pose any problems at all. However, if you're starting to use actors not part of the standard Unreal package, you will face some difficulties. A 3rd party gun, for instance, will become invisible on the clients, won't feature any new sounds and so forth, but it still can be used. Now, if the client hasn't previously downloaded the gun, that wouldn't be surprising - unfortunately, the problem persists even if the gun is located on the client's system..! Fortunately, there is a way around this.
By including any actor that is to be used by a Mutator in the ServerPackages list, you'll enable clients to see and use them without any side effects. Although this is a somewhat awkward solution, especially if you use a number of different Mutators on different occasions, it's the only way to solve the problem.

  1. Open Unreali.ini in a text editor.
  2. Scroll down 'til you see a group called "[Engine.GameEngine]"
  3. A few lines below this header, you should see several lines starting with "ServerPackages="
  4. Add a new line for each package you wish to include (e.g. adding "MyMod.u" would require a line reading "ServerPackages=MyMod")

If you want to create a Dedicated Server, all you need to do is to add "-server" at the end of the command line, i.e. "unreal://mymap.unr?mutator=mymod.mymutator?game=gamemod.gameclass -server"

The Limits of Mutators

While Mutators are quite useful, they do have some limits at this time:

  • They can't really replace true game types, nor were they ever intended to.
  • Their usefulness comes from the fact that you can link a number of Mutators and combine them with a game type.
  • Replacing some actors will produce strange results, including critical errors. If the code attempts to refer to the actor by using variables (i.e. a "handle"), stuff might get unpredictable at best. For instance, replacing a Player Pawn isn't a good idea - the GameInfo stores the return value (which is the "handle" referring to the specific player pawn spawned) and as the Mutator in effect destroys the pawn, the handle will become obsolete. Any attempt to refer to the late player pawn, for instance when the code tries to place the player at a respawn point, will fail utterly.

Final Words

Well, this was a quick introduction to Unreal's Mutators. Hopefully, it wasn't too confusing and will help some coders out there to get started :)

If you wish to contact me, you may do so at bane@unrealized.com.

Cheers!
Bane


Mutator Tutorial

Squidis Mutator Tutorial

Section 1: What is a Mutator
Section 2: What is the Purpose of this Tutorial
Section 3: The Design Phase
Section 4: Configuring Your Mutator
Section 5: Mutators Don't Have to Player Nice, but they should...
Section 6: The Mod Menu

Appendix - The Mutator Class

1) What is a Mutator?

Mutators are special classes which modify the game being played in some way. Normally, mutators are single minded in their goals, such as just making the players fat, or reducing the gravity. Mutators also have another sellling point. They can be linked together, provided they are properly written. This allows you to more fully customize your gaming experience. For instance, it allows you to be fat AND have low gravity. There is a limit to a mutator's ability to change things around, but there are still lots of really cool things you can do with it.

2) What is the Purpose of this Tutorial?

I've seen a lot a people attempt to write mutators, and I've seen a lot of people do some things which can cause problems. The purpose of this tutorial is to explain some features which may be overlooked, as well as provide a nice little list of Do's and Dont's for the aspiring mutator developer. Don't just think of this document as a tutorial...think of it as a style guide.

What this tutorial won't do, unfortunately, is tell you how to use UnrealEd to make classes. The focus of this tutorial is mutators, and nothing but. I don't want to bog things down with extraneous information. This tutorial assumes that you can examine the code from the built-in mutators to figure out the basic outline of how a mutator works. There isn't any hand-holding in this document.

3) The Design Phase

Mutators allows coders a lot of freedom in what can be done. However, one of the most important aspects to keep in mind is that mutators should be limited in scope. If you make them do too much, they won't work well with other mutators. If you provide configuration windows and menus, then you are complicating the process too much. A mutator should be something that is added to a game to change one particular aspect.

Don't create a mutator which replaces every weapon in the game with a new one, then modifies the game so that you only have 3 lives. First, the mutator really does two things. It replaces the weapons and modifies the game. What if the player wants to just use the weapons, or just wants the 3 lives aspect? That should really be two seperate mutators. If you absolutely think the two go together, then you are better off creating a GameType and not a mutator. GameTypes can't be linked together, and therefore are more suited for coherent game playing experiences. Mutators mutate. They aren't meant to be the end experience.

Second, why replace all the guns? What if the player wants to only use one or two from the set, and keep the normal guns? What if the player wants to mix and match guns from different mutator sets? The simple answer here is to split each weapon up into its own mutator. This is similar to what Epic did with the Relic mutators. Rather than having a single Relic mutator, there is a mutator for each relic. That way, you can select which relics you want to add into the game at the mutator window. This rule can apply to anything which has several options that are either on or off. For instance, if you made a mutator which added in monsters to a deathmatch level, you could create a mutator for each monster type. This saves you from having to create an unwieldly configuration dialog.

When creating gun mutators, try to replace the gun which is most similar. The weapons in UT are fairly balanced in that the different weapons appeal to different styles of play. There isn't a whole lot of need for two sniper-esque weapons. Also, the player is guarranteed to be familiar with the default weapon set. By keeping in sync with that (sniper rifles are weapon 9, melee is weapon 1, pistols 2, etc), the player can quickly handle any new weapons you can throw at him. This also has the side effect of the player being able to decide which weapons he wants. He can take the sniper rifle from the realistic weapons pack, and the pistols from the sci fi weapons pack. He can mix and match to his hearts content.

You may want to release a whole bunch of melee weapons. For instance, you may want to release a pack with swords and maces and stuff like that. Since there is really only one melee spot, what you should do is provide two ways to get at the weapons. First, provide mutators for every weapon which replaces the default melee weapon. Second, provide a GameType or second Mutator which replaces all the weapons with those mutator weapons. By doing this, the player can decide whether he wants to choose one weapon from the bunch, or just use the bunch as provided. Remember, the point of mutators is to make the game more flexible, not less so.

Finally, if you are significantly changing the gameplay, you might be better off with a GameType. It allows you to do things that mutators don't, and it is completely apparent to the player that gameplay is different. The one problem here is that if you want to change each GameType (Deathmatch, Team, CTF, Assault, etc), you need a new gametype for each kind. In this case, you obviously aren't modifying the game at a basic level (CTF isn't going to change into Assault), so this is an area where mutators are better suited. You would also need a new GameType for each of those. If everyone released 5 gametypes for every little modification, things could get really ugly. I'll leave it up to your judgement. But if you do a GameType, please see my gameType style guide tutorial first (coming soon).

4) Configuring Your Mutator

One thing about Unreal Tournament is that it allows developers to create UWindows (the built in GUI) to allow customization of mutators and mods. You can add in a menu item on the Mod Menu which allows the player to open up this configuration. My suggestion concerning this is simply: DON'T DO IT. If you've kept your mutator simple, and followed my other suggestions, then you shouldn't need to configure anything. Mutators shouldn't be configured. It is an entirely unwieldly experience selecting a mutator and then configuring it each game.

If you've got some options, and multiple mutators or a GameType (which can be configured from custom settings and rules windows) won't cover it, then you could just not make them options. If there is a choice on a subject like how many lives you get, there are several things you can do. You can provide several mutators (3 lives, 5 lives, 9 lives), or you could just hardcode what you think is the best option (5 lives?). If they player never realizes he has a choice, he won't miss not having one.

Say you have a mutator which allows you to change certain attributes of the player. You can select any fatness number you want, and drawscale number, and things like that. Being able to type in any number may seem really powerful, but it is just making things harder for the player. Think about it. What kind of fatness will the player try. He will try skinny, not so skinny, really fat, and pretty fat. You don't need to allow the player to select a fatness of 100.45. Same with scale. The player would want to play at half size, and probably at slightly larger than normal (double size is too large).

Ultimately, the plan is that mutator space is cheap. It takes two seconds to search a list and select a mutator. It takes considerably longer to open a dialog and configure it, especially when you have numbers to type in. It is regrettable that Epic didn't allow Mutator lists like it has Map Lists. That would be a good project for someone to look into.

5) Mutators Don't Have to Play Nice, but they should...

In some functions, you need to make sure you call the parent mutator's function. Some of these just call the next mutator in the list, but even that is important. If you fail to call the next mutator, then it won't work properly. People will think it is a flaw in the other mutator when, in fact, it is your mutator with the flaw. Try to work with other mutators when at all possible. You'll be glad you did. Remember, if a mutator isn't nice, you never know where it will be in the mutator list. It could be first, or it could be last. All this means is that some other mutator might have removed items you need long before the flow even gets to you.

Some mutators don't play nice. For instance, the Arena mutator remove all the weapons in the game and give the players a single type. This can lead to some incompatibilies with other mutators. For instance, try running InstaGib with Guns Akimbo mutator, and bad stuff happens. The thing about Arena mutators is that they do something really simple and to the point. However, this can affect other mutators. I suggest that if your mutator does something that can cause problems, you put it in the readme file. Before you release your mutator, test it will all the mutator that come with Unreal Tournament and test for compatibility issues. If you are feeling really lucky, test it with popular 3rd party mutators too. It never hurts to be too cautious.

6) The Mod Menu

If you absolutely feel the need to put a menu item into the mod menu, there are some style tips that I suggest. First, don't do it. The larger the menu gets, the less effective it becomes. By putting in a menu item, you are potentially making the player's life harder. Mutators should be so simple that they don't need menu items. If every mutator released had some configurable options, there would be hundreds of items on the mod menu! Hundreds! I know UWindows is a neat thing, and using it makes your mod look more professional, but ultimately, if you don't need it, don't put it in. Even mod authors who create new game types can put configurable options in the game settings and rules tabs in the game creation dialog.

Don't take up more than one mod menu item. It should go without saying, but don't take up more space than you need. If you have several configurable options, it would be better to have a single window with tabbed pages for each option set. If you want, you can even create a window which will open and allow you to have your own menu bar. The window could even display a nice logo or something. Don't think your mutator is more important than anyone else's.

For your one menu item, name it something short and to the point. If your mutator/mod is called "Terminator", call the menu item "Terminator". Don't bother putting things in the menu title which confuses it, like "Terminator Options" or "Configure Terminator Mutator". Just the single word is more than enough to convey what mod/mutator that menu pertains too. Oh, and don't use abbreviations in the name, if at all possible. I've downloaded mutators that had one name, and their mod menu title was something completely different. As it is, I've got mod menu items I have no idea where they came from or what they do. Avoid this.

Appendix: The mutator class in detail


Mutator Appendix

Squidi's Guide to Making Mutators

Now to the fun part. Explaining the Mutator class. The class descends from Info, which
is an Actor, and so Mutator inherits all of these variables and properties. It doesn't
use most of them, but some of them, such as Actor's Tick() function, can be useful.
Anyway, on to the explaining.

Variables

-----------------

bool bHUDMutator;
This variable is set to true when RegisterHUDMutator() succeeds. Defaults to false.
Don't bother setting this variable. It's only use is as listed.
class

 DefaultWeapon;
Set this class the change the default weapon (the one the player starts off with).
This variable is actually used in the MutatedDefaultWeapon() function set, and
is available to reduce that whole process down to a single variable. This variable
is how the Arena mutators work their magic.

Functions

-----------------

event PreBeginPlay();
This function is called just before the game begins. Because of the way mutators
are set up, this function can be called twice, so if you have any important things
in here that should've be done twice, you should create a boolean variable like so:
        if( bInitialized ) return;
        bInitialized = true;
Popular things to do in this function are set up variables and register this mutator
for different things (such as HUD mutators or damage mutators).

EXAMPLE
-------
    event PreBeginPlay() {
        if (bInitialized) return;
        bInitialized = True;

        numLives = 3;		// initialize some variables
        RegisterHUDMutator();// Set self to receive PostRender() calls

        if( bHUDMutator )	// if RegisterHUDMutator() worked, bHUDMutator is true
        {
            log("Ok, I get PostRender() calls.");
        }
    }

function PostBeginPlay();
This function is not actually part of the mutator, but is instead inherited from the
Actor class. It is called after PreBeginPlay(). It is actually very similar to
PreBeginPlay(), except that it is called after certain aspects of the game have
been initiallized. Use this function to set up variables (just like PreBeginPlay),
especially if you have variables that need to be set after the game is initialized
(like in the example below).

EXAMPLE (from Botpack.DMMutator.uc)
-----------------------------------
    function PostBeginPlay()
    {
        MyGame = DeathMatchPlus(Level.Game);
        Super.PostBeginPlay();
    }

simulated event PostRender( canvas Canvas );
This function allows you to draw on the HUD. Use the Canvas passed to draw anything
you want on the HUD. This will draw directly on top of the existing HUD, which
allows your mutator to affect all HUDs with the same call. This function reduces
the need to actually create a new HUD. This function will only be called if you
first call RegisterHUDMutator() in the PreBeginPlay() function and bHUDMutator is
true. See the canvas class for drawing functions.

EXAMPLE (From RelicHUDMutator.uc, simplified for example purposes)
------------------------------------------------------------------
    simulated function PostRender(canvas C) {
        local float Scale;

        if ( MyRelic != None )	//myRelic is a mutator global variable
        {
            Scale = ChallengeHUD(PlayerOwner.MyHUD).Scale;
            C.DrawColor = ChallengeHUD(PlayerOwner.MyHUD).HUDColor;
            C.SetPos(C.ClipX - 64 * Scale, C.ClipY - 192 * Scale);
            C.DrawIcon(MyRelic.Icon, Scale);
        }
    }

function ModifyPlayer(Pawn Other);
This function is called every time a player begins a new life. Use this function to
change aspects of the player. For instance, you can reset a player's fatness, or
make him start out really skinny. If you want to affect a player when they die/kill
someone, use ScoreKill() instead.

EXAMPLE
-------
    function ModifyPlayer(Pawn Other)
    {
        Other.fatness = fatness;		// change player attributes

        Super.ModifyPlayer(Other);	// Be nice, call Super.ModifyPlayer()
    }

function ScoreKill(Pawn Killer, Pawn Other);
This function is called whenever someone dies (the Pawn Other). Killer is the
Pawn which killed Other. If Killer == none, or Killer == Other, then it was
a suicde of some sort. You can use this function to make things happen when
one player kills another (ie they get fatter). If you want to affect a player when
they first start out each life, use ModifyPlayer() instead.

EXAMPLE (from Botpack.FatBoy.uc, simplified for example)
--------------------------------------------------------
    function ScoreKill(Pawn Killer, Pawn Other)
    {
        if ((Killer != Other) && (Other != None) && (Killer != None))
        {
            if (Killer.Fatness >= 240)	// When someone is killed, their killer
                Killer.Fatness = 255;	// slowly gets fatter.
            else
                Killer.Fatness += 10;
        }

        Super.ScoreKill(Killer, Other);	// Be nice, call Super.ScoreKill()
    }

function Class MutatedDefaultWeapon();
You do not need to play with this function. Just set the variable DefaultWeapon.
function Class MyDefaultWeapon();
You do not need to play with this function. It is used internally.
function AddMutator(Mutator M);
You do not need to play with this function. It is used internally. The Arena mutators,
do override this to make sure that no other arena mutators are added to the mutator
list. The following snippet is provided if you are planning something similar.

EXAMPLE (From Botpack.Arena.uc)
-------------------------------
    function AddMutator(Mutator M)
    {
        if ( M.IsA('Arena') )
        {
            log(M$" not allowed (already have an Arena mutator)");
            return; //only allow one arena mutator
        }
        Super.AddMutator(M);
    }

function bool ReplaceWith(actor Other, string aClassName);
This function allows you to replace an actor of one class, with an actor of
another class. Call this from CheckReplacement(). You don't need to override
this function.
function bool AlwaysKeep(Actor Other);
This function is called with every Actor added to the game, including lights and
ZoneInfos and weird stuff like that. If you return a true from this function, that
actor will always be kept, even if other mutators want to get rid of it. This
partly allows you to insist on certain actors being available. If you return false,
that just means that you don't NEED the actor and don't care what happens to it. The
actor may be added, or some other mutator may get rid of it.

EXAMPLE
-------
    function bool AlwaysKeep(Actor Other) {
        if(Other.IsA('FlakAmmo'))	// Always keep Flak Ammo. Other mutators
            return true;		// cannot replace or remove Flak Ammo now.

        return Super.AlwaysKeep(Other);// Play nicely, call the super.AlwaysKeep()
    }

function bool IsRelevant(Actor Other, out byte bSuperRelevant);
This function calls CheckReplacement(). You don't need to modify this function.
function bool CheckReplacement(Actor Other, out byte bSuperRelevant);
This function allows you to decide which items get added to the game. Each actor
added to the game gets pass to this function, and you can decide whether you
want to keep it (return true), not keep it (return false), or replace it with
another class althogether (call ReplaceWith(Other, 'class'), then return false).
Remember, when you replace something, that new object gets passed to this function
too. So do have circular replacements (A replaces B, B replaces A). Also, watch
out for subclasses (see example). By setting bSuperRelevant to true, the actor
will be kept no matter what. You can use AlwaysKeep() to make sure an actor won't
be removed/replaced by other mutators.

EXAMPLE
-------
    function bool CheckReplacement(Actor Other, out byte bSuperRelevant) {
        if ( Other.IsA('WarHeadLauncher') )
            return false;				  // Fine

        if ( Other.IsA('Weapon') ) {
            ReplaceWith(Other, "ChainSaw");  // Bad - Chainsaw is subclass of Weapon
            return false;
        }

        if ( Other.IsA('Weapon') && !Other.IsA('ChainSaw') ) {
            ReplaceWith(Other,"ChainSaw");  // Good - affects all weapons but Chainsaw
            return false;
        }

        return true;
    }

function Mutate(string MutateString, PlayerPawn Sender);
This command allows you to create console commands. Whenever a player types in the
console, "mutate" all the current mutators get that string. You can decide
what you want to do with that. For instance, a player could type "mutate drop
armor" and your mutator could do something about it. Players can bind keys to
console commands, so you can use this function to add new control keys to your
mutator. It can also be used to configure your mutator in the game.

EXAMPLE
-------
    function Mutate(string MutateString, PlayerPawn Sender)
    {
        if( MutateString~="GetFat")	// Player typed "mutate GetFat" at console.
        {
            Sender.fatness = 255;
        }

        Super.Mutate(MutateString,Sender); // Be nice
    }

function MutatorTakeDamage( out int ActualDamage, Pawn Victim,
       Pawn InstigatedBy, out Vector HitLocation, out Vector Momentum,
       name DamageType);
This function sure has a lot of parameters! It is called whenever any pawn receives
any damage from any source. I do not have a lot of experience in this function, but
I would assume this would be where "Head Shots" or immunity to Lava Damage would
come in.

EXAMPLE (from the Vampire Mutator in the UT Mod Doc at unreal.epicgames.com)
----------------------------------------------------------------------------
    function MutatorTakeDamage( out int ActualDamage, Pawn Victim,
                                Pawn InstigatedBy, out Vector HitLocation,
                                out Vector Momentum, name DamageType)
    {
        if (InstigatedBy.IsA('Bot') || InstigatedBy.IsA('PlayerPawn'))
        {
            InstigatedBy.Health += ActualDamage;
            if (InstigatedBy.Health > 199)
            InstigatedBy.Health = 199;
        }

        Super.MutatorTakeDamage(ActualDamge, Victim, InstigatedBy,
            HitLocation, Momentum, DamageType);
    }

simulated function RegisterHUDMutator();
Thus function registers this mutator with the game so that it can start receiving
PostRender() function calls. bHUDMutator will be set to true if this function
succeeds. Don't override this one.


HUD Mutator Tutorial

As you may have noticed, the HUD class has a HUDMutatorvar that can be used to call a Mutator to add features to the HUD. Unfortunately, the normal ModifyPlayer can't be used to set up each player's HUD in a network game. It works fine on the server side, but none of the clients get a modified HUD. This is because calls the MyHUD of players who aren't locally owned always returns a None.

You can however use the SpawnNotify class to catch and modify HUD's (or anything else) when they are spawned. SpawnNotify is called on both client players and server players, so it works for all. Here's how it goes:

Your main mutator should spawn a SpawnNotify Class. The SpawnNotify adds itself to everyone's level object automatically, insuring it gets called when a new object is spawned. Everytime a HUD is created, you catch it in the SpawnNotify Class and spawn a copy of your hud mutator, seting the HUD's HUDMutator variable to this new object. (Or to the NextMutator var of any Mutator already attached to the HUD.)

Here is sampe source code. I have tested the code out - should allow multiple mutators that use this scheme to all link togeather and all work correctly. Please read through carefully, if you have any questions, email me. Feel free to use, modify, or redistribute the source, although I would prefer it if you left some comments in so the next guy can learn from your work.

Special thanks to Necron_99 for pointing out the AddMutator function. Makes this whole thing a lot more straightforward.

/=============================================================================
//  HUDTestMut is jsut a sample of how to get your SpawnNotify Class spawned.
//   This will be the Mutator you select from the Mutator Menu, so make sure
//   you .int file references this, not the HUDMut.
//
//  Andrew "drunken master" Scholer - ascholer@mediaone.net
//=============================================================================

class HUDTestMut expands Mutator;

function PreBeginPlay()
{
      spawn(class'asHUDTest.asHUDNotify');
}

defaultproperties
{
}

//=============================================================================
//  HUDMut is the mutator you will spawn for each actor to handle
//   the PostRender calls. Note that if there is another HUDMutator
//   attached to it it passes on the function call, allowing the
//   other mutator to do its work also.
//
//  Obviously, to coexist well with other HUDMutators, it might be a good
//   idea to make you draw location adjustable. If you display some status
//   indicators, try creating Mod Menu configurable choices between
//   displaying on the right, left or top of screen. That way user can set up
//   a few different HUD modifying mutators and know they won't overwrite
//   each other on screen.
//
//  Andrew "drunken master" Scholer - ascholer@mediaone.net
//=============================================================================

class HUDMut expands Mutator;

simulated function PostRender(canvas Canvas)
{
    //Do your cool stuff here instrad of this test code.

   Canvas.SetPos(50,50);
   Canvas.DrawText("This is a test");

    //If there is someone chained to this Mutator, call them
    // NextMutator is a variable of the Mutator class and will be set
    // up by the AddMutator call below.

    if ( NextMutator != None ) //next mutator is already defined
    {
         NextMutator.PostRender(Canvas);
    }
}

defaultproperties
{
}

/=============================================================================
// SpawnNotify - Actor spawn notification.
//   NB - This happens on the client AND server for replicated actors.
//
//  As many of you may have noticed, trying to get at MyHUD for clients in
//   a network game through ModifyPlayer dosen't work. Instead you have to
//   use a SpawnNotify actor. Once spawned, this guy sits around and intercepts
//   all spawn hotifications of a given class (Selected in default properties).
//   Note the simulated function call - this guy works on both server and clients.
//
//  I added code to allow HUDmutators to chain off one another. This depends
//   on your HUDMutator class having a variable HUDMutator (see HUDMut). If
//   you really don't want to play nice, just can the else statement below.
//   Then only the first mutator in gets to use it's HUD.
//
//  Andrew "drunken master" Scholer - ascholer@mediaone.net
//=============================================================================

class asHUDNotify expands SpawnNotify;

//Called automatically anytime the class you specified in default properites
// is spawned (also called if a subclass of that class is spawned).
//Great way to catch things to modify or replace them - just make sure to use
// if statements to avoid infinite recursion if you replace something by
// spawning a new subclass.

simulated event Actor SpawnNotification( Actor A )
{
    local Mutator tempActor;

    if ( HUD(A).HUDMutator == none ) {
        //If there aren't any HUDMutators attached to the HUD, attach
        // your mutator.
        HUD(A).HUDMutator = spawn(class'asHUDTest.HUDMut',A);
     }
    else
    {
        //If there is a HUDMutator attached to the HUD already, call
        // AddMutator on that hud, passing it a newly spawned mutator
        // of your own. Note that the AddMutator function will
        // automatically seek the end of the mutator chain. (If the
        // one you try to add yourself to has a NextMutator, the
        // function call is passed to that mutator.)
        HUD(A).HUDMutator.AddMutator(spawn(class'asHUDTest.HUDMut',A));
    }

        //Have to return the original actor (the HUD). If we had
        // completely replaced it we would return the replacement.
    return A;
}

defaultproperties
{
    //This selects what class to intercept spawns for.
    ActorClass=class'Engine.HUD'
}


UWindows Help

UWindows Options

Mutators with configuration options should make their own UWindow subclasses for configuration, and insert themselves into the (usually invisible) Mods menu, instead of using advanced options. Here is a really basic outline of how you'd go about it. I haven't compiled this code but it's pretty much everthing you should need to do. (JbP: I've since compiled the code. It works :) When I get some time I hope to document the uwindow classes so it's a bit more obvious where to start doing stuff like this.

MyMutatorConfigWindow

class MyMutatorConfigWindow expands UWindowFramedWindow;

function Created()

{

Super.Created();

SetSize(150, 100);

WinLeft = (Root.WinWidth - WinWidth) / 2;

WinTop = (Root.WinHeight - WinHeight) / 2;

}

defaultproperties

{

ClientClass=class'MyMutatorConfigClient'

WindowTitle="UDamageArena Configuration!"

}

MyMutatorConfigClient

class MyMutatorConfigClient expands UWindowDialogClientWindow;

var UWindowCheckBox SomeCheck;

function Created()

{

Super.Created();

SomeCheck = UWindowCheckBox(CreateControl(class'UWindowCheckBox', 10, 10, 100, 1));

SomeCheck.SetText("Some Option");

SomeCheck.SetFont(F_Normal);

SomeCheck.Align = TA_Left;

SomeCheck.bChecked = class'MyMutatorClass'.default.bSomeOption;

// You could of course add heaps of controls in here.

// See the UMenu and UTMenu for a billion examples.

}

function Notify(UWindowDialogControl C, byte E)

{

switch(E)

{

case DE_Change:

switch(C)

{

case SomeCheck:

class'MyMutatorClass'.default.bSomeOption = SomeCheck.bChecked;

class'MyMutatorClass'.static.StaticSaveConfig();

break;

}

break;

}

}

MyMutatorMenuItem

class MyMutatorMenuItem expands UMenuModMenuItem;

function Execute()

{

MenuItem.Owner.Root.CreateWindow(class'MyMutatorConfigWindow', 10, 10, 150, 100);

}

defaultproperties

{

MenuCaption="&UDamageArena Configuration"

MenuHelp="Configure the UDamageArena mutator! Make ALL your pickups UDamages!!!!"

}

The int file

Finally, you need to add this to your mutator's .int file to add yourself into the menus.

[Public]

Object=(Name=MyMutatorPkg.MyMutatorMenuItem,Class=Class,MetaClass=UMenu.UMenuModMenuItem)

-------------------------------------------------

JbP's Notes:

1) The int file has a reference to MyMutatorPkg. This should be changed to the name of your package.

2) To compile the above directly, you will need to create the mutator which these classes reference. It is called MyMutatorClass:

class MyMutatorClass expands Mutator;

var bool bSomeOption;

3) If you've never used ucc to compile uc classes before, the 'defaultproperties' sections above will be unfamiliar to you. They won't compile inside UnrealEd. You will need to compile the above using ucc. Heres the simple explanation:

  1. Go into your UnrealTournament.ini file and find the section containing the EditPackages= statements. There should be about 10 of them. Add EditPackages=MyMutatorPkg at the end of this list.
  2. Make a sub-directory of your UT directory called MyMutatorPkg. Make a sub-directory of this, called Classes.
  3. Save each class as a .uc file in the classes directory. You should have four text files, called MyMutatorMenuItem.uc, MyMutatorClass.uc, etc. The name of the uc file should be the name of the class.
  4. Open up a dos prompt, cd to your UT\System directory, and type ucc make. It should eventually either compile your code or return an error, indicating you need to check your code or your files setup. Warnings will result in compiled code, but if you ignore warnings be prepared for buggy mods.

4) Thanks, Mek!


UWindows Tutorial

What is UWindows?

Probably the first thing about Unreal Tournament you'll notice is how much spiffier the interface is. Rather than having the Wolfenstein 3D throwback menu system, UT has implemented its own small Graphical User Interface (GUI). It has windows, menus, a mouse pointer, scrollbars, and a lot of other things which other GUIs have (such as Windows). GUI interfaces are famous for making information easier to convey, and harder to program.

Luckily, UT's interface isn't really all that hard to program. If you understand some basic object orientated principles, you shouldn't have much trouble with it. If you've programmed for Windows, Java, or the Macintosh, you'll find that UT's interface is much more simple and easier to use.

UWindows may or may not be the official title of the GUI interface. The core classes all have names with the prefix UWindow, like UWindowButton or UWindowTabControlLeft. For obvious reasons, I call the interface UWindows.

How does UWindows work?

UWindows makes up the core engine of the graphics interface. It does not include any game specific classes. This allows mod makers and liscencees to make their own games with the Unreal engine without having all that extra game specific code. UT's interface exists as a layer or two above the UWindows core. Right above UWindows is the UMenu interface. It provides the basic interface with the help system, menu bar, and configuration dialogs. Built on top of UMenu is UTMenu, which is the specific classes used in Unreal Tournament.

When working with UWindows, you really shouldn't find much need to mess around with the UMenu and UTMenu layers unless you are trying to specifically change a feature of Unreal Tournament. Working with classes outside of the standard UWindows core group could mean compatibility problems with other game engines. Creating a mutator configuration window could actually work between Unreal Tournament, Wheel of Time, and even Nerf Blast Arena if you never stray past the standard classes.

It all begins with UWindowBase?

The entire class tree for UWindows, UMenu, and UTMenu all start with a single node: UWindowBase. All classes in the UWindows system stem from this single point, and they all inherit what is in it. As such, UWindowBase doesn't really have a whole bunch of code. It provides several constants and enumerations that are used amonst the bunch.

After UWindowBase, the class tree splits three ways. Probably the least important path of the three is UWindowLookAndFeel. It provided the hooks for building in the look and feel of the application. Maybe you've noticed in UT, you can change your Look and Feel between Gold, Ice, and Metal? Well, each of those Look and Feels are objects under this class. They define how buttons are drawn, and what a menu looks like, and so on. Nerf Blast Arena even has its own Look and Feel. However, the provided L&Fs should be more than enough to satisfy most ideas.

The second branch past UWindowBase is the UWindowList tree. None of the classes under UWindowList are graphical objects. UWindowList provides a basic linked list functionality for its children classes. Most of the child classes are specific lists used in the GUI, such as a list of menu items, or the list of strings in a combo box. There really isn't a whole lot to monkey around with in this section, although you do need to subclass at least one thing here.

The third and final branch of the tree is UWindowWindow. While it says window, it really isn't. In this case, the window it is refering to is a rectangular graphic. This could be a window window, but this could also be a menu, or a button. All objects under UWindowWindow are visible objects that can be messed with by either the mouse or the keyboard. To distinguish between a UWindowWindow and a window with a title bar that can be moved around, I shall refer to the latter as a Framed Window.

The really cool thing about everything descending from UWindowWindow is that anything under this class can have anything else in this class inside it. You can have Framed Windows with menu bars, and menu bars with buttons, and even buttons that pop up menus. By careful construction of the class tree, Epic hsa made it very functional and very easy to use.

So everything is a window?

All visible graphical objects are descended from UWindowWindow. They all share the same properties and values defined in this class. Easily the most important of these are four variables: WinLeft, WinTop, WinWidth, and WinHeight. These variables form the rectangle that the window occupies. There are a few boolean variables of note, such as bAlwaysOnTop, bAlwaysBehind, and bWindowVisible.

There are several functions in UWindowWindow which don't do anything, but are worth pointing out. Functions like BeginPlay(), BeforeCreate(), Created(), AfterCreate(), Activated(), and Resized() allow their functionallity to be filled out by the children classes. You might override the Created() function of a window to add controls to it. Just be sure that when you override a function to call the parent function too (ie Super.Created()).

How do I start?

This tutorial assumes you are already a little familiar with UScript, and have a mutator or two done. The provided sample code assumes a mutator called sqFatMutator with two variables: fatness and bUseTwoGuns.

The first thing you need is a starting place. UT needs to know when and how to create a window for you. Luckily, UT makes this easy by providing a built in starting place: the MOD menu. Normally, this menu is invisible, but shows up when you add your first item to it. You create your own spot on the menu by subclassing UMenuModMenuItem (from the UWindowList tree). Notice that it is a UMenu class and not a UWindow one. The UMenuModMenuItem has only two functions: Setup() and Execute(). Here is the code for a sample mod menu item (remember to change the class names and what-not to your classnames first).

One the class is completed and compiled, add a line similar to the following to your .int file:

[Public]

Object=(Name=SquidiMods.sqFatModMenuItem,Class=Class, MetaClass=UMenu.UMenuModMenuItem)
------------------------------------------------------------------

class sqFatModMenuItem expands UMenuModMenuItem;

function Setup()

{

//The MenuCaption variable is used to store the menu item's name on

// the menu. Unfortunately, the menu item is created before Setup()

// is called, and thus uses the default property. Still, this is

// provided as a reference.

MenuCaption = "FatMutator Config";

//MenuHelp is simply the string written in the status bar at the bottom

// of the screen when the mouse hovers over this menu item. Luckily

// for us, setting the help caption here DOES work.

MenuHelp = "Sets up the FatMutator";

}

function Execute()

{

//Create a window using the following function:

// CreateWindow( class, top, left, width, height )

//The window I will create will automatically resize itself to

// the right proportions (I'll show you how when we get to the

// Framed Window part), so it doesn't matter what the top, left,

// width or height variables are. Just the class portion matters.

MenuItem.Owner.Root.CreateWindow(class'sqFatConfigWindow',

10,10,10,10);

}

------------------------------------------------------------------

It didn't compile!

When UnrealEd tries to compile this code, it'll point out that you haven't
written the sqFatConfigWindow class yet. So lets do that. What we are going
to do is subclass UWindowFramedWindow to create a nifty little frame which
we drag around the screen to our heart's content.

------------------------------------------------------------------

class sqFatConfigWindow expands UWindowFramedWindow;
function BeginPlay()

{

Super.BeginPlay();
//Set the title of the Framed Window

WindowTitle = "Configure Fat Mutator";

//The class of the content

ClientClass = class'sqFatClientWindow';

//Make the Framed Window resizable

bSizable = true;

}

------------------------------------------------------------------

Most of this code is pretty self explanatory, with the exception of the
ClientClass variable. If this variable were set to None, you'd see that
when the window was created you would just get the framed part. The insides
would show through to the background! A FramedWindow is just that, a frame.
When ClientClass is set to a valid client window class, when the window is
created, that class will be created along with it. You don't need to worry
about making both of them.

You'll find the class UWindowClientWindow just under UWindowWindow, but
that isn't the client window type we are going to use. A child class of
ClientWindow, UWindowDialogClientWindow has a few extra important functions
required to use controls. So we are going to subclass that one instead. It
also has an extra ability to suggest a desired width and height for the
content. Just subclassing UWindowDialogClientWindow is enough to fill in
the empty space in the Framed Window.

Ooh. How do I add buttons and stuff?

Content is added in the form of controls. All controls are children of
UWindowDialogControl (which in turn is a child of UWindowWindow). So all
controls are also windows. There are dozens of controls to choose from. Some
of the controls share functionality (for instance UWindowCheckbox and
UWindowSmallButton are both children of UWindowButton), but most have
individual options to tweak. Controls are created, and added to your client
window via the UWindowDialogClientWindow.CreateControl() function that
you'll have inherited. It has the same parameters as the CreateWindow()
function. Here is a somewhat modified version of my sample client window
class:

-----------------------------------------------------------------

class sqFatClientWindow expands UWindowDialogClientWindow;
var UWindowCheckBox checkbox;

var UWindowHSliderControl slider;
// called after object has been created...add content

function Created()

{

Super.Created();
//Add checkbox via the CreateControl() function.

checkbox = UWindowCheckBox(

CreateControl(class'UWindowCheckBox', 10, 10, 150, 1));

checkbox.SetText("Two Guns");

checkbox.bChecked = true;

//create slider in same fashion

slider = UWindowHSliderControl(

CreateControl(class'UWindowHSliderControl', 10, 30, 150, 1));

slider.SetText("Fatness");

slider.SetRange(1,255,5);

slider.SetValue(128,true);

}
//when a control changes, Notify is called with the changed control

// and the message sent.

function Notify(UWindowDialogControl C, byte E)

{

switch(E)

{

case DE_Change: // the message sent by sliders and checkboxes

switch(C)

{

case slider:

//get slider value

break;

case checkbox:

//get checkbox info

break;

}

break;

}

}

------------------------------------------------------------------

The Notify() is how you know when one of your controls has changed in
value. This allows you to update the variables the controls were supposed
to reflect. The message sent in this case is the DE_Change one, which is
the message most often used. You can also use Notify() to update the text
on the controls. For instance, the fatness control can change from skinniness
to fatness depending where the slider is.

Great! That's just about it. How do I interact with my mutator?

To make the config dialog more useful, those levers and buttons actually
need to affect something in your mutator. This is accomplished by setting
the default values. This can easily be accessed via a line similar to this:

class'SquidiMods.sqFatMutator'.default.fatness = slider.GetValue();
class'SquidiMods.sqFatMutator'.static.StaticSaveConfig();

The final version of my client window sample follows:

-----------------------------------------------------------------

class sqFatClientWindow expands UWindowDialogClientWindow;
var UWindowCheckBox checkbox;

var UWindowHSliderControl slider;
// called after object has been created...add content

function Created()

{

Super.Created();
//Add checkbox via the CreateControl() function.

checkbox = UWindowCheckBox(

CreateControl(class'UWindowCheckBox', 10, 10, 150, 1));

checkbox.SetText("Two Guns");

checkbox.bChecked =

class'SquidiMods.sqFatMutator'.default.bUseTwoGuns;

//create slider in same fashion

slider = UWindowHSliderControl(

CreateControl(class'UWindowHSliderControl', 10, 30, 150, 1));

slider.SetText("Fatness");

slider.SetRange(1,255,5);

slider.SetValue(

class'SquidiMods.sqFatMutator'.default.fatness, true);

}
//when a control changes, Notify is called with the changed control

function Notify(UWindowDialogControl C, byte E)

{

local int sval;

switch(E)

{

case DE_Change: // the message sent by sliders and checkboxes

switch(C)

{
case slider:

sval = slider.GetValue();

class'SquidiMods.sqFatMutator'.default.fatness = sval;

class'SquidiMods.sqFatMutator'.static.StaticSaveConfig();

if( sval > 0 ) slider.SetText("Fatness [Skinny]");

if( sval > 50 ) slider.SetText("Fatness [Slim]");

if( sval == 128) slider.SetText("Fatness [Normal]");

if( sval > 175 ) slider.SetText("Fatness [Fat]");

if( sval > 200 ) slider.SetText("Fatness [Fatboy]");

break;
case checkbox:

class'SquidiMods.sqFatMutator'.default.bUseTwoGuns

= SomeCheck.bChecked;

class'SquidiMods.sqFatMutator'.static.StaticSaveConfig();

break;

}

break;

}

}

-----------------------------------------------------------------

Read Part Two of this tutorial: Advanced UWindows Techniques


UWindows Tutorial 2

UWindows Tutorial Part 2

Part 1: Before We Begin
Part 2: Creating and Using Menus
Part 3: Tabbed Pages
Part 4: Scrolling Client Areas

Part 1: Before We Begin

This tutorial assumes you are already familiar with the basics of programming the UWindows system. This includes making windows and client windows, adding controls to windows, adding a spot on the modmenu, and generally understanding the terminology. When I talk about ClientWindows and FramedWindows, I am going to assume that you know the difference between them. As such, I will not walk you through the process of doing these things. Please see my introductory tutorial for that type of help.

All of my class names begin with 'sq' (for Squidi), and then an uppercase abbreviation of what that particular item is. ClientWindows are CW, Framed Windows are FW, Menu Bars are MB, Menus are M, and so on. Then is the mod type (in this case "Tutorial"). After that, if it is necessary, is a description of the object. sqFWTutorial and sqCWTutorial are sufficient enough to distinguish a difference. However, individual menus might be called sqMTutorialGame and sqMTutorialHelp.

Part 2: Creating and Using Menus

To use menus in your configuartion windows, you need a way to get to the menu. This can be accomplished through a MenuBar, or through using Right Clicks to bring up a pop up menu. MenuBars are a little easier follow, so we'll start there. To create a Menu (and MenuBar), you must subclass the appropriate object (UWindowMenuBar and UWindowPulldownMenu). Since both MenuBars and Menus are descendents from UWindowDialogControl, they can both exist inside framed windows, and are created using the CreateControl() function.

In the following examples, I haven't implemented the tooltips helpbar that the main menu bar makes. It makes things a little more complicated. If you want to add in this help functionality, checkout UMenuGameMenu and the likes. Also, I won't go into Right Click menus at this time, as I'm unsure how Mac users do the right click. I don't want to promote an activity that I'm not sure everyone can participate in.

//====================================================================

// sqMBTutorial.

//====================================================================

class sqMBTutorial expands UWindowMenuBar;
var UWindowPulldownMenu Game, Help;

var UWindowMenuBarItem GameItem, HelpItem;
function Created()

{

Super.Created();
bAlwaysOnTop = False;
GameItem = AddItem("Game");

Game = GameItem.CreateMenu(class'sqPMTutorialGame');

// For this menu, we are using the built in HelpMenu

HelpItem = AddItem("Help");

Help = HelpItem.CreateMenu(class'UMenuHelpMenu');
Spacing = 12;

}

/**

** Adding in the following code will make the menuBar drawn in the

** current look and feel, as opposed to a generic gray one

**

** function DrawMenuBar(Canvas C)

** {

** LookAndFeel.Menu_DrawMenuBar(Self, C);

** }

**

**

**/

//====================================================================

// sqPMTutorialGame.

//====================================================================

class sqPMTutorialGame expands UWindowPulldownMenu;

var UWindowPulldownMenuItem NewGame, Load, Save, GameOptions, Quit;

function Created()

{

Super.Created();
// Add menu items.

NewGame = AddMenuItem("New Game", None);

Load = AddMenuItem("Load", None);

Save = AddMenuItem("Save", None);

AddMenuItem("-", None);

Quit = AddMenuItem("Quit", None);

}

function ExecuteItem(UWindowPulldownMenuItem I)

{

switch(I)

{
case NewGame: // new game menu item was selected

break;

case Load: // Load menu item was selected

break;

case Save: // Save Menu Item was selected

break;

case Quit: // Quit menu item was selected

break;

}
Super.ExecuteItem(I);

}

Add the MenuBar to your window with the following code (in the clientwindow's Created() function):

MenuBar = sqMBTutorial(CreateWindow(class'sqMBTutorial', 0, 0, WinWidth, 16));

And that's about it. You may want to put some code in the client window's Resized() function to change the width of the menuBar when the window changes sizes. Otherwise, the user may make the window larger than the menuBar, and you'll see empty space. Another thing of note is that you don't need to add a menuBar to the top of a window. You can put it anywhere. Just remember that the pulldown menus will drop below the menuBar no matter where the location is.

Part 3: Tabbed Pages

An example of Tabbed Pages can be seen in the preferences dialog. The window has several tabs at the top of the window which will select between the different pages of configurable options. Making a TabbedPage is incredibly easy. You just need to create a control of type UWindowPageControl, and then add the different pages to it. Each page is just a client window of type UWindowPageWindow. Since UWindowPageWindow is descended from UWindowDialogClientWindow, you create them in the same fashion as you would a normal client window. In fact, if you aren't really sure whether or not your client window will ever be used in a tabbed page, you can go ahead and make it a subclass of UWindowPageWindow anyway. The change is transparent to UWindows, and saves you from having to change parent classes if you decide later on to put it under a tab.

To create a tab control, just do the following in your client window's Created() function:

Pages = UWindowPageControl( CreateWindow(class'UWindowPageControl', 0, 0, WinWidth, WinHeight));

To add a page to the tab bar, just use the following code, where XXX is the class name of the Page Window you want to add. And that's it. The most important thing to remember is to subclass UWindowPageWindow for your client window needs.

Pages.AddPage("Test", class'XXX');

Part 4: Scrolling Client Areas

Maybe you've noticed that when you resize windows, the controls and content don't change. They are obscured by the edges of the window when it gets too small. One way to change this is to resize each of the controls when the window changes in size. My client window has the following code to make sure the menu bar and tabbed controls stay with proportion to the window's size. The function Resized() is called whenever the client area changes size.

function Resized()

{

Super.Resized();
MenuBar.WinLeft = 0;

MenuBar.WinTop = 0;

MenuBar.WinWidth = WinWidth;

MenuBar.WinHeight = 16;

Pages.WinTop = 20;

Pages.WinLeft = 10;

Pages.WinWidth = WinWidth - 20;

Pages.WinHeight = WinHeight - 30;

}

This works great because the menu bar and tab area always have a size relative to the window. But in cases where you have controls and other client windows, you can't always change their size small enough. The solution to this is to provide a scrollable area. The class UWindowScrollingDialogClient (which is a Page Window) provides scrollbars which allows its own client area to remain the same size and be scrolled around in.

They are really easy to use. Just like a framed window, they have a client class variable which will create the specified client area. This time the client window doesn't need to be a PageWindow. Here is some sample code (lifted straight out of UMenuHUDConfigScrollingClient)

class UMenuHUDConfigScrollClient extends UWindowScrollingDialogClient;

function Created()

{

ClientClass = Class<UMenuPageWindow>(DynamicLoadObject( GetPlayerOwner().MyHUD.HUDConfigWindowType, class'Class'));
Super.Created();

}

The scrolling client exists as an invisible layer between the client area, and the container in which the client area exists. Most of UT's dialogs use scrolling clients for the internal client areas. Then if the area is too large, or the user sets big fonts, the user can still get to every control. You should really use scrolling clients whenever you get the chance, as they add functionality which either is invisible to the user, or makes his life a lot easier. It does, however, add one more class to your user interface for each client area which uses it.


Vectors and Rotators

This tutorial is rather simple, but important. Vectors and Rotators are vital to the understanding of how items are spawned in UT. This tutorial only attempts to define vectors and rotators as viewed by the spawn function. It may not be accurate in all instances not related to the spawn function, so please keep that in mind.

Since we are going to be looking at vectors and rotators in relation to the spawn function, let's first get a rundown of that function. The prototype is this:

native(278) final function actor Spawn

(

class<actor> SpawnClass,

optional actor SpawnOwner,

optional name SpawnTag,

optional vector SpawnLocation,

optional rotator SpawnRotation

);

When I first looked at the above, it confused the heck out of me, so let me explain it.

The SpawnClass variable is what you want to be spawned, i.e. a Rocket from a rocket launcher or a grenade from a grenade launcher. This is the only parameter that is required. In your code, it will look like this:

spawn(class'yourPack.yourItem');

The SpawnOwner variable is the person who the item is spawned from. Thus, if a player shot a rocket, the rocket's owner could be placed in the SpawnOwner spot. This variable is optional, and most people don't need it. Code:

spawn(class'yourPack.yourItem', yourSpawnOwner);

The SpawnTag variable is confusing even now, and I'm not sure what exactly it does yet.

SpawnLocation is where the item is spawned at in the level. It is a vector, and we will look at it in more detail later. Code:

spawn(class'yourPack.yourItem', yourSpawnOwner,,yourSpawnLocation);

If you noticed, you will see that there are two commas after yourSpawnOwner in the code above. The reason for that is that in order to bypass the SpawnTag variable, you have to include an extra comma to tell UT that yourSpawnLocation is associated with the SpawnLocation variable, and not the SpawnTag variable. I know that sounds confusing, so here is another example:

spawn(class'yourPack.yourItem',,,yourSpawnLocation);

Here, both the SpawnOwner variable and the SpawnTag variable are skipped, thus the extra two commas. I hope this makes it clear to you.

Finally, on to the last variable, SpawnRotation. This is a rotator, and we will also look at this in more detail. First, the code:

spawn(class'yourPack.yourItem',,, yourSpawnLocation, yourSpawnRotator);

I've gone ahead and done away with the yourSpawnOwner variable, as it is hardly used, but you may find a use for it.

Vector Variable SpawnLocation in more detail:

SpawnLocation is where in the level your item will be spawned. It is a vector, and every vector has 3 parts: an X, a Y, and a Z component. The X, Y, and Z of the vector makes up a 3d point in a UT level that the object you spawn appears at. Usually, this is the same location as the actor that spawns the object. Every actor in a level has a default property called Location. To access that point, you would write something like this:

local pawn yourPawn;

local vector spawnLoc;

spawnLoc = yourPawn.Location;

In this code, spawnLoc is set to the point in the level where yourPawn is. Of course this is very simplified code, but you get the idea.

There are a few ways to change the variables contained in a vector. The easiest (but longest) way is to change the individual components individually. Basically:

spawnLoc.X += 3;

spawnLoc.Y += 4;

spawnLoc.Z += 5;

The other way is to change the vector by using another vector:

local vector addVect;

spawnLoc += addVect;

Of course, addVect has to be initialed first, but you get the idea.

You must be careful about just assigning a vector arbitrary values, because you can never be sure where the vector is in the level. If you set a vector in your code to be (0,0,0), then anything spawned using that vector will spawn at (0,0,0)...however, that point may be in the middle of a rock in one level, or right at a team's flag, or whatever. Therefore, most vectors should be used in relation to a point that you can know. Such as taking a player's Location variable, assigning another vector that location, and then modifing the new vector to do what you want.

The final thing is an explaination of the various components of the vector. The X portion of it is the horizontal part. The Y portion is up and down, and the Z portion is horizontal as well, perpendicular to the X component.

Rotator Variable SpawnRotation in more detail:

SpawnRotation defines the direction that your spawned item will travel if it does travel. In fact, the only reason you will need a SpawnRotation is to define the direction that an object will travel, or the direction your object will face (although I'm not sure about the last one). Every rotator has 3 parts: a pitch, a yaw, and a roll. Every actor in UT has a default property called Rotation, which is accessed exactly like the Location variable in the sample code above.

A rotator can also be changed by the same methods as a vector. The only difference is the properties involved:

local rotator newRot;

newRot.Yaw += 3;

newRot.Pitch += 4;

newRot.Roll += 5;

A rotator can be assigned arbitrary values, and a certain value means a certain thing. The Yaw component is used to define the horizontal aspect of the rotator. A full circle is 65,535. You may define a Yaw component higher than 65535, but that will just begin to overlap the same circle that has already been used. For example, if you wanted an object to spin around 2 times, the final value of the Yaw component will be 131070. You can also define negative values, and that is like going around the circle in reverse.

Pitch is the vertical aspect of the rotator. A value of 0 is horizontal. Anything up to 32767 is like the equivalent of 0 to 89 degrees. A value of 32767 is straight up, and anything over 32767 is the equivalent of 91-180 degrees (i.e. the projectile will go backwards). A negative value will make the projectile go down instead of up. Also, anything over 65535 will make the projectile go down as well, until the value passes 131070, and so on and so forth.

Roll I don't really understand yet (I've never used it), but I think it is used to make your projectile spin.

All of the values I've listed above I got from trial and error in coding. They may not be 100% correct, but they are very close.


Unreal Console Commands

Unreal Tournament Console Commands

Jack Porter
Epic Games, Inc.
http://www.epicgames.com/
Based on the Unreal 1 console commands document.

Audience: Advanced Users, Server Administrators, Programmers.
Last Updated: 10/17/99

Introduction

This is a list of console commands and command line options, most of which are valid for the current version of the UT demo. The upcoming version of the demo will add a few of the new ones. The cheat codes will be changed for the full version of UT.

UnrealTournament.exe Command Line Parameters

-changevideo | Bring up the video card selection menu
-conflicts | Show objects of different classes whose names conflict.
-log | Runs with the log window initially visible.
-noddraw | Disables DirectDraw support.
-nodsound | Disables DirectSound support.
-nogc | Disables garbage collection.
-nohard | Disables 3d hardware support.
-nok6 | Disable AMD K6-3D support.
-nolog | Don't show log window.
-nokni | Disables KNI (Pentium III) support
-nommx | Disables MMX support.
-nosound | Disables sound.
-nowarn | Disables warning message boxes on the screen.
-profile | When C++ is compiled with DO_SLOW_GUARD=1, generates an UnrealScript function profile using the timings in the most recently played level only.
-safe | Runs Unreal in "safe mode", avoiding loading any device drivers which could crash if there are hardware/driver problems.
-server | Runs as a dedicated server. See ucc server
-silent | Run silently, with no log window, and no display of error messages. Critical errors just cause the engine to exit silently.
INI=inifilename | Specify the .ini file to use for configuration. Normally UnrealTournament.ini.
USER=userinifilename | Specify the .ini file to use for user-specific configuration. Normally User.ini
LOG=logfilename | Uses the specified log file instead of UnrealTournament.log. Useful when running multiple copies on the same machine.
MULTIHOME=12.34.56.78 | Sets the "home" ip address (in numerical format) which Unreal Tournament will bind to, for a machine with multiple IP addresses.
PORT=num | Sets the UDP port number for the Internet server.
READINI=inifilename | Sets the .ini file for reading only (not for writing); overrides the INI= option.

Ucc.exe Command Line Parameters

UCC.exe is the command line UnrealTournament launcher. It launches a number of "commandlets". ucc help will give you a list of available commandlets.

Ucc make

-h | Rebuilds all .u files from their source .uc files and generate a C++ header for each package.
-nobind | Don't treat UnrealScript/C++ binding errors as critical, for rebuilding scripts and avoiding the chicken-and-egg binding problem.
-strict | Treat all script warnings as critical errors; useful for tracking down the call stack when debugging scripts.

Ucc server

Usage: ucc server URL [parameters]
Most of the parameters valid for unrealtournament.exe are valid for ucc server.
The URL parameter is usually just a map name, but can include a number of options:

  • ?alladmin - give all players administration access
  • ?game= -specify the game class
  • ?mutator= - specify a list of mutator classes (comma separated)

UnrealScript Console Commands

General console commands

MENUCMD <menu> <menuitem> | Perform an UnrealTournament menu item. For example, MENUCMD 1 0 brings up the Unreal Tournament server browser
SAY <message string> | Broadcast a message to other players in the game.
SETNAME <newname> | Change your name (works in network play too).
SETTEAM <newname> | Change your team (works in network play only when the rules allow).
SUICIDE | Commit Suicide
TEAMSAY <message string> | Broadcast a message to your teammates.

Practice session console commands/cheats. Most of these will change.

ADDBOTS <number> | Adds more bots to the game.
ALLAMMO | Gives you all the ammo for your current weapons.
FLY | Fly around (instead of walking). See WALK.
GHOST | Fly around and go through walls (see WALK).
GOD | Makes you invincible.
KILLALL <class name> | Kills all actors of a certain class, for example "KILLALL WARHEADLAUNCHER".
KILLPAWNS | Kills all bots
PLAYERSONLY | Pauses all non-player actors in the game. Cool for debugging scripts, taking screenshots.
SLOMO <number> | Sets the game speed, 1.0 is normal.
SUMMON <class name> | Spawns an actor of the specified class (for example, SUMMON BOTPACK.WARHEADLAUNCHER) in front of the player.
VIEWCLASS <class name> | Cycle through actors of this class, viewing from their locaton. eg VIEWCLASS BOT
WALK | Walk around normally (see FLY, GHOST).

Admin console commands. Some of the above practice session cheats are available in network play when you're logged in as an administrator.

ADMIN <command> | Perform a console command on the server's console
ADMIN SERVERTRAVEL <new level URL> | Causes the server to switch to a new level.
ADMIN SET UWeb.Webserver bEnabled True | Enable the remote admin webserver (requires a level change to take effect)
ADMIN SET UWeb.Webserver bEnabled False | Disable the remote admin webserver (requires a level change to take effect)
ADMINLOGIN password | Login as an administrator
ADMINLOGOUT | Drop administrator access
KICK <playername> | Kick a player
KICKBAN <playername> | Kick a player and ban his/her IP. You'll need to edit your ini file or use the WWW remote admin to unban their IP

C++ Console Commands

These only work on the local machine.

BRIGHTNESS | Cycle through 10 screen brightness values.
CANCEL | Cancels an "open" command in progress that is trying to connect to a network server.
CDTRACK num | Play the specified CD track.
CPUSPEED=# | Identify your CPU speed, useful for machines which don't autodetect their speed properly. For example, CPUSPEED=266.
DEMOPLAY <filename>
[?noframecap]
[?timebased]
[?3rdperson] 		Play a recorded demo. noframecap plays the demo back as fast as possible. This option is assumed if timedemo 1 is enabled. timebased gives a smoother playback if the playback machine is significantly slower than the recording machine. 3rdperson lets you fly around a first person demo as a spectator. eg demoplay mydemo?3rdperson?timebased
DEMOREC <filename> | Record a demo to <filename>
DISCONNECT | Disconnect from the current server
ENDFULLSCREEN | Go back to playing in a window.
EXEC filename | Execute the console commands in the filename, default is in the System directory.
EXIT | Exit the game..
FLUSH | Flush all caches. Regenerates all lighting, 3d hardware textures, etc. Can be useful to clear texture corruption due to 3D hardware driver bugs
FOV angle | Set the field of view, for example "FOV 90" for 90 degrees.
GETCOLORDEPTHS | Get a list of color depths supported by your 3D hardware
GETCURRENTCOLORDEPTH | Get the current color depth (16 or 32)
GETCURRENTRES | Get the current resolution
GETRES | Get a list of available resolutions
HIDEACTORS | Hide actors during game play.
JUMPTO x y z | In UnrealEd, move the viewport to a certain X,Y,Z location, useful in conjunction with tester reports generated with the REPORT command.
MUSICORDER num | Change to a certain track in the song (0=ambient, 1=action, 2=suspense).
NETSPEED number | Change your desired network data rate. Use 2600 for modem, 5000 for ISDN, 20000 for Cable/ADSL/LAN
OPEN url | Opens a local map (eg "dm-turbine.unr") or an Internet server (eg "200.0.1.16" or "unreal.epicgames.com").
s_occfactor | A3D 2.0 occlusion factor
s_maxnodedist | A3D 2.0 maximum node distance
s_reflect 0 or 1 | A3D 2.0 reflections
s_occlude 0 or 1 | A3D 2.0 occlusions
s_wavetracing 0 or 1 | A3D 2.0 Wavetracing
s_refgain | A3D 2.0 reflection gain
s_refdelay | A3D 2.0 reflection delay
s_maxpoly | A3D 2.0 maximum number of polygons
s_maxreflectpoly | A3D 2.0 maximum number of reflection polygons
s_polysmall | A3D 2.0 smallest polygon size
s_polylarge | A3D 2.0 large polygon size
s_a3dsources | A3D 2.0 Sources
SETRES WxHxD | Set the resolution and color depth. eg SetRes 800x600x32
SERVERTRAVEL url | Change levels from the UnrealTournament Server console window.
STOPDEMO | Stop recording or playing back a demo.
PREFERENCES | Displays the Advanced Options window.
RECONNECT | Reconnect to the current/previous server
SHOT | Take a screenshot and save it in the System directory with a consecutive name like Shot0001.bmp.
TOGGLEFULLSCREEN | Toggles fullscreen mode on and off.
TYPE | Types text on the console.

Also see the Mesh LOD document for Mesh LOD console commands.

Statistics

STAT ACTOR | Shows various statistics.
STAT CACHE |
STAT CLIP |
STAT FILTER |
STAT FPS | Show frame time in milliseconds
STAT GAME |
STAT GLOBAL | Shows useful performance timings in milliseconds
STAT HARDWARE |
STAT ILLUM |
STAT LIGHT |
STAT MESH |
STAT NET | Show network performance statistics including ping, packet loss, bytes/second
STAT POLYC |
STAT POLYV |
STAT REJECT |
STAT OCCLUSION |
STAT SOFT |
STAT SPAN |
STAT ZONE |

Debugging

DEBUG CRASH | Test crashing the game by appError.
DEBUG EATMEM | Test allocating memory until it fills up.
DEBUG GPF | Test a general protection fault.
DEBUG RECURSE | Test crash by infinite recursion.
DUMPCACHE | Display the contents of the memory cache GCache.
DUMPNATIVES | Display a list of unused native function ids.
EDITACTOR CLASS=classname | During game play, edits the properties of the nearest actor belonging to that class. Cool for debugging.
GET <classname> <variable> | Gets the default value for a variable in a class
MARK | Mark all objects that are in memory (for garbage collection debugging in conjunction with MARKCHECK).
MARKCHECK | Display a list of all unmarked objects that are in memory (for garbage collection debugging in conjunction with MARK).
MEM | List memory usage; only works when Unreal is compiled with the memory-tracking option (it's usually not).
MEMSTAT | Show Windows overall memory statistics.
OBJ CLASSES | Prints a list of all loaded classes.
OBJ DEPENDENCIES PACKAGE=pkgname EXCLUDE1=otherpackage EXCLUDE2=otherpackage... | Show package dependencies
OBJ GARBAGE | Collect and purge garbage ("garbage" means objects which are no longer in use).
OBJ HASH | Show object hashing statistics.
OBJ LINKERS | Show a list of active linkers (each linker is responsible for loading objects from a package file).
OBJ LIST CLASS=objectclass INSIDE=insideclass | List all objects belonging to the class and residing inside the specified object. If the class isn't specified, lists ALL objects. If the inside object isn't specified, shows all objects. Gives a summary of memory usage. This is very useful during game play for figuring out how much memory is being used. In UnrealEd, this statistic isn't useful because a huge amount of extra stuff is loaded which might not be used by your map.
OBJ REFS CLASS=objectclass NAME=objectname | Show a list of objects which reference the object you specify. When trimming memory usage, this is useful for figuring out why some object is being loaded.
REPORT | Copies a report of the current gameplay situation to the clipboard. You can then paste the resulting text into an email program, Notepad, etc. Extremely useful for beta testers, because it dumps the player's X,Y,Z location, the difficulty level, etc.
RMODE | Set the rendering mode. 0-9.
SET <class> <variable> <value> | Sets the specified property of the specified class with the specified value
SHOWACTORS | Shows actors during gameplay.
SOCKETS | In network play, shows a list of network sockets (UNetConnection's) in use.
URL urlname | In network play, parses a URL and displays its components.

Unreal Tournament Key Bindings

In the Unreal Tournament key bindings (type PREFERENCES to bring up Advanced Options / Advanced / Raw Key Bindings), you can associate console commands and other special input commands with key presses and releases. In the text box next to a key name, you can type in one or more console commands, separated by the "|" character. For example, to bind the "S" key to the "Jump" alias, type this:

    Jump

That causes the "Jump" alias to be executed when th user presses the "S" key. For another example, if you want to bind a chat message to a key, use this:

    Say "Come get some!"

In addition, you can bind multiple actions to a keypress like this, to make the "S" key both jump and say a message.

    Jump | Say "Come get some!"

You can also bind an action to the release of a key by using the OnRelease keyword. For example, you can make the "S" key fire when it's released:

    OnRelease Fire

Or you can make the key jump when pressed, and fire when released:

    Jump | OnRelease Fire

Keys can be bound to any of the following kinds of things:

  • Any console command in the above lists.
  • Any key alias (explained below).
  • Any special input command.

"Special input command" refers to console commands which only make sense in conjunction with the press or release of a key, or the movement of the mouse or joystick along an axis. The special input commands are:

  • BUTTON <UnrealScript boolean variable name>, for example "BUTTON bFire": Maps a button to an UnrealScript variable stored in the Pawn class or any subclass. This tells the input system to automatically set the variable to True when the key is held down, and False when it's not pressed. For example, the Pawn script looks at bFire to tell whether the fire button is being held. The setting of bFire is not some hardcoded behavior of the engine, rather it is propagated into UnrealScript code by the input system, as a result of binding a key to "BUTTON bFire".
  • TOGGLE <UnrealScript boolean variable name>, for example "TOGGLE bLook": Like the BUTTON command, but simply tells the input system to toggle the boolean variable's value when the key is pressed.
  • AXIS <UnrealScript floating point variable name> <SPEED=some number>, for example "Axis aStrafe Speed=-300.0". Maps an analog input axis movement to an UnrealScript variable, scaling it by some factor. The current axis keys listed in the Advanced Options menu are: MouseX, MouseY, MouseW (Intellimouse wheel), JoyX, JoyY, JoyZ, JoyR (joystick rudder), JoyU, JoyV (joystick alternate axis for example the Panther XL trackball). Supported PlayerPawn axes are aBaseX, aBaseY (base movements which are translated to rotating, strafing, looking, and forward movement based on context), aMouseX, aMouseY, aForward, aTurn, aStrafe, aUp (not used), aLookUp, aExtra0-aExtra4 (not used). To see how the game code processes movement, see the PlayerPawn class's PlayerInput and PlayerTick functions.

In addition to the Advance Options menu, you can also bind keys from the console using the following command:

    SET INPUT <keyname> <binding>

For example:

    SET INPUT X Fire
    SET INPUT Enter Fire

Unreal Tournament Key Aliases

Key aliases are listed in Advanced Options / Advanced / Input Aliases. Aliases provide a convenient way to map one word (for example, "Jump") to a complex series of console commands that carry out a particular action. For example, the "Fire" alias is defined as: "Fire | Button bFire". This has the effect of calling the UnrealScript "PlayerPawn.Fire" function (aliases are not recursive), and then causing the input button bJump to be set to True as long as the key is held.


UnrealScript Language Reference

Tim Sweeney
Epic MegaGames, Inc.
tim@epicgames.com
http://www.epicgames.com/

Last Updated: 12/21/98

Introduction

Purpose of this document

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 prior to release of Unreal, the reader is referred to the source code to the Unreal scripts, 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 printing out the "Actor", "Object", "Pawn", "Inventory", and "Weapon" scripts.

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

For programmers who are new to OOP, I highly recommend going to Amazon.com or a bookstore and buying 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-inheretance 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 frustraging 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.

Example program structure

This example illustrates a typical, simple UnrealScript class, and it highlights the syntax and features of UnrealScript. Note that this code may differ from that which appears in the current Unreal source, as this documentation is not synced with the code.

//=============================================================================
// TriggerLight.
// A lightsource which can be triggered on or off.
//=============================================================================
class TriggerLight expands Light;

//-----------------------------------------------------------------------------
// Variables.

var() float ChangeTime; // Time light takes to change from on to off.
var() bool bInitiallyOn; // Whether it's initially on.
var() bool bDelayFullOn; // Delay then go full-on.

var ELightType InitialType; // Initial type of light.
var float InitialBrightness; // Initial brightness.
var float Alpha, Direction;
var actor Trigger;

//-----------------------------------------------------------------------------
// Engine functions.

// Called at start of gameplay.
function BeginPlay()
{
    // Remember initial light type and set new one.
    Disable( 'Tick' );
    InitialType = LightType;
    InitialBrightness = LightBrightness;
    if( bInitiallyOn )
    {
        Alpha = 1.0;
        Direction = 1.0;
    }
    else
    {
        LightType = LT_None;
        Alpha = 0.0;
        Direction = -1.0;
    }
}

// Called whenever time passes.
function Tick( float DeltaTime )
{
    LightType = InitialType;
    Alpha += Direction * DeltaTime / ChangeTime;
    if( Alpha > 1.0 )
    {
        Alpha = 1.0;
        Disable( 'Tick' );
        if( Trigger != None )
            Trigger.ResetTrigger();
    }
    else if( Alpha < 0.0 )
    {
        Alpha = 0.0;
        Disable( 'Tick' );
        LightType = LT_None;
        if( Trigger != None )
            Trigger.ResetTrigger();
    }
    if( !bDelayFullOn )
        LightBrightness = Alpha * InitialBrightness;
    else if( (Direction>0 && Alpha!=1) || Alpha==0 )
        LightBrightness = 0;
    else
        LightBrightness = InitialBrightness;
}

//-----------------------------------------------------------------------------
// Public states.

// Trigger turns the light on.
state() TriggerTurnsOn
{
    function Trigger( actor Other, pawn EventInstigator )
    {
        Trigger = None;
        Direction = 1.0;
        Enable( 'Tick' );
    }
}

// Trigger turns the light off.
state() TriggerTurnsOff
{
    function Trigger( actor Other, pawn EventInstigator )
    {
        Trigger = None;
        Direction = -1.0;
        Enable( 'Tick' );
    }
}

// Trigger toggles the light.
state() TriggerToggle
{
    function Trigger( actor Other, pawn EventInstigator )
    {
        log("Toggle");
        Trigger = Other;
        Direction *= -1;
        Enable( 'Tick' );
    }
}

// Trigger controls the light.
state() TriggerControl
{
    function Trigger( actor Other, pawn EventInstigator )
    {
        Trigger = Other;
        if( bInitiallyOn ) Direction = -1.0;
        else Direction = 1.0;
        Enable( 'Tick' );
    }
    function UnTrigger( actor Other, pawn EventInstigator )
    {
        Trigger = Other;
        if( bInitiallyOn ) Direction = 1.0;
        else Direction = -1.0;
        Enable( 'Tick' );
    }
}

The key elements to look at in this script are:

  • The class declaration. Each class "expands" (derives from) one parent class, and each class belongs to a "package", a collection of objects that are distributed together. All functions and variables belong to a class, and are only accessible through an actor that belongs to that class. There are no system-wide global functions or variables.
  • The variable declarations. UnrealScript supports a very diverse set of variable types including most base C/Java types, object references, structs, and arrays. In addition, variables can be made into editable properties which designers can access in UnrealEd without any programming.
  • The functions. Functions can take a list of parameters, and they optionally return a value. Functions can have local variables. Some functions are called by the Unreal engine itself (such as BeginPlay), and some functions are called from other script code elsewhere (such as Trigger).
  • The code. All of the standard C and Java keywords are supported, like "for", "while", "break", "switch", "if", and so on. Braces and semicolons are used in UnrealScript as in C, C++, and Java.
  • Actor and object references. Here you see several cases where a function is called within another object, using an object reference.
  • The "state" keyword. This script defines several "states", which are groupings of functions, variables, and code which are executed only when the actor is in that state.
  • Note that all keywords, variable names, functions, and object names in UnrealScript are case-insensitive. To UnrealScript, "Demon", "demON", and "demon" are the same thing.

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 which 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, not from code within a function.

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 which interacts with UnrealScript.

All UnrealScripts execute in parallel. If there are 100 monsters walking around in a level, all 100 of those monsters' scripts are executing simultaneously and independently.

Class overview

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 hardcoded 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 expansion 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 is 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 (expands Object) is 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 (expands Actor) is the parent class of all creatures and players in Unreal which are capable of high-level AI and player controls.

Class (expands Object) is 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.

The class declaration

Each script corresponds to exactly one class, and the script begins by declaring the class, the class’s parent, and any additional information that is relevent to the class. The simplest form is:

class MyClass expands MyParentClass;

Here I am declaring a new class named "MyClass", which inherets the functionality of "MyParentClass". Additionally, the class resides in the package named "MyPackage".

Each class inherets all of the variables, functions, and states from its parent class. It can then add new variable declarations, add new functions (or override the existing functions), add new states (or add functionality to the existing states).

The typical approach to class design in UnrealScript is to make a new class (for example a Minotaur monster) which expands 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.

The class declaration can take several optional specifiers that affect the class:

  • native: Says "this class uses behind-the-scenes C++ support". Unreal expects native classes to contain a C++ implementation in the DLL corresponding to the class’s package. For example, if your package is named "Robots", Unreal looks in the "Robots.dll" for the C++ implementation of the native class, which is generated by the C++ IMPLEMENT_CLASS macro.
  • Abstract: Declares the class as an "abstract base class". This prevents the user from adding actors of this class to the world in UnrealEd, because the class isn’t meaningful on its own. For example, the "Pawn
    base class is abstract, while the "Brute" subclass is not abstract – you can place a Brute in the world, but you can’t place a Pawn in the world.
  • guid(a,b,c,d): Associates a globally unique identifier (a 128-bit number) with the class. This Guid is currently unused, but will be relevent when native COM support is later added to Unreal.
  • transient: Says "objects belonging to this class should never be saved on disk". Only useful in conjunction with certain kinds of native classes which are non-persistent by nature, such as players or windows.
  • config(section_name): If there are any configurable variables in the class (declared with "config" or "globalconfig"), causes those variables to be stored in a particular configuration file:
    • config(system): Uses the system configuration file, Unreal.ini for Unreal.
    • config(user): Uses the user configuration file, currently User.ini.
    • config(whatever): Uses the specified configuration file, for example "whatever.ini".

Config(configname)

Variables

Simple Variables

Here are some examples of instance variable declarations in UnrealScript:

var int a; // Declare an integer variable named "A".
var byte Table[64]; // Declare an array of 64 bytes named "Table".
var string[32] PlayerName; // Declare a max 32-character string.
var actor Other; // Declare a variable referencing an actor.

Variables can appear in two kinds of places in UnrealScript: instance variables, which apply to an entire object, appear immediately after the class declarations. Local variables appear within a function, and are only active while that function executes. Instance variables are declared with the "var" keyword. Local variables are declard with the "local" keyword.

Here are the basic variable types supported in UnrealScript:

  • byte: A single-byte value ranging from 0 to 255.
  • int: A 32-bit integer value.
  • bool: A boolean value: either "true" or "false".
  • float: A 32-bit floating point number.
  • string: A string of characters.
  • name: The name of an item in Unreal (such as the name of a function, state, class, etc). Names are stored as a 16-bit index into the global name table. Names correspond to simple strings of 1-31 characters. Names are not like strings: strings can be modified dynamically, but names can only take on predefined name values.
  • Enumeration: A variable that can take on one of several predefined name values. For example, the ELightType enumeration defined in the Actor script describes a dynamic light and takes on a value like LT_None, LT_Pulse, LT_Strobe, and so on.
  • Object and actor references: A variable that refers to another object or actor in the world. For example, the Pawn class has an "Enemy" actor reference that specifies which actor the pawn should be trying to attack. Object and actor references are very powerful tools, because they enable you to access the variables and functions of another actor. For example, in the Pawn script, you can write "Enemy.Damage(123)" to call your enemy’s Damage function – resulting in the enemy taking damage. Object references may also contain a special value called "None", which is the equivalent of the C "NULL" pointer: it says "this variable doesn’t refer to any object".
  • Structs: Similar to C structures, UnrealScript structs let you create new variable types that contain sub-variables. For example, two commonly-used structs are "vector", which consists of an X, Y, and Z component; and "rotator", which consists of a pitch, yaw, and roll component.

Variables may also contain additional specifiers such as "const" that further describe the variable. Actually, there are quite a lot of specifiers which you wouldn’t expect to see in a general-purpose programming language, mainly as a result of wanting UnrealScript to natively support many game- and environment- specific concepts:

  • const: Advanced. Treats the contents of the variable as a constant. In UnrealScript, you can read the value of const variables, but you can’t write to them. "Const" is only used for variables which the engine is responsible for updating, and which can’t be safely updated from UnrealScript, such as an actor’s Location (which can only be set by calling the MoveActor function).
  • input: Advanced. Makes the variable accessible to Unreal’s input system, so that input (such as button presses and joystick movements) can be directly mapped onto it. Only relevent with variables of type "byte" and "float".
  • transient: Advanced. Declares that the variable is for temporary use, and isn’t part of the object’s persistent state. Transient variables are not saved to disk. Transient variables are initialized to zero when an actor is loaded.
  • native: Advanced. Declares that the variable is loaded and saved by C++ code, rather than by UnrealScript.
  • private: The variable is private, and may only be accessed by the class's script; no other classes (including subclasses) may access it.

Arrays are declared using the following syntax:

var int MyArray[20]; // Declares an array of 20 ints.

UnrealScript supports only single-dimensional arrays, though you can simulate multidimensional arrays by carrying out the row/column math yourself.

In UnrealScript, you can make an instance variable "editable", so that users can edit the variable’s value in UnrealEd. This mechanism is responsible for the entire contents of the "Actor Properties" dialog in UnrealEd: everything you see there is simply an UnrealScript variable, which has been declared editable.

The syntax for declaring an editable variable is as follows:

var() int MyInteger; // Declare an editable integer in the default category.
var(MyCategory) bool MyBool; // Declare an editable integer in "MyCategory".

Object and actor reference variables

You can declare a variable that refers to an actor or object like this:

var actor A; // An actor reference.
var pawn P; // A reference to an actor in the Pawn class.
var texture T; // A reference to a texture object.

The variable "P" above is a reference to an actor in the Pawn class. Such a variable can refer to any actor that belongs to a subclass of Pawn. For example, P might refer to a Brute, or a Skaarj, or a Manta. It can be any kind of Pawn. However, P can never refer to a Trigger actor (because Trigger is not a subclass of Pawn).

One example of where it’s handy to have a variable refering to an actor is the Enemy variable in the Pawn class, which refers to the actor which the Pawn is trying to attack.

When you have a variable that refers to an actor, you can access that actor’s variables, and call its functions. For example:

// Declare two variables that refer to a pawns.
var pawn P, Q;

// Here is a function that makes use of P.
// It displays some information about P.
function MyFunction()
{
    // Set P’s enemy to Q.
    P.Enemy = Q;

    // Tell P to play his running animation.
    P.PlayRunning();
}

Variables that refer to actors always either refer to a valid actor (any actor that actually exists in the level), or they contain the value "None". None is equivalent to the C/C++ "NULL" pointer. However, in UnrealScript, it is safe to access variables and call functions with a "None" reference; the result is always zero.

Note that an object or actor reference "points to" another actor or object, it doesn’t "contain" an actor or object. The C equivalent of an actor reference is a pointer to an object in the AActor class (in C, you’d say an AActor*). For example, you could have two monsters in the world, Bob and Fred, who are fighting each other. Bob’s "Enemy" variable would "point to" Fred, and Fred’s "Enemy" variable would "point to" Bob.

Unlike C pointers, UnrealScript object references are always safe and infallible. It is impossible for an object reference to refer to an object that doesn’t exist or is invalid (other than the special-case "None" value). In UnrealScript, when an actor or object is destroyed, all references to it are automatically set to "None".

Class Reference Variables

In Unreal, classes are objects just like actors, textures, and sounds are objects. Class objects belong to the class named "class". Now, there will often be cases where you'll want to store a reference to a class object, so that you can spawn an actor belonging to that class (without knowing what the class is at compile-time). For example:

var() class C;
var actor A;
A = Spawn( C ); // Spawn an actor belonging to some arbitrary class C.

Now, be sure not to confuse the roles of a class C, and an object O belonging to class C. To give a really shaky analogy, a class is like a pepper grinder, and an object is like pepper. You can use the pepper grinder (the class) to create pepper (objects of that class) by turning the crank (calling the Spawn function)...BUT, a pepper grinder (a class) is not pepper (an object belonging to the class), so you MUST NOT TRY TO EAT IT!

When declaring variables that reference class objects, you can optionally use the special class<classlimitor> syntax to limit the variable to only containing references to classes which expand a given superclass. For example, in the declaration:

var class<actor> ActorClass;

The variable ActorClass may only reference a class that expands the "actor" class. This is useful for improving compile-time type checking. For example, the Spawn function takes a class as a parameter, but only makes sense when the given class is a subclass of Actor, and the class<classlimitor> syntax causes the compiler to enforce that requirement.

As with dynamic object casting, you can dynamically cast classes like this:

class<actor>( SomeFunctionCall() )

Enumerations

Enumerations exist in UnrealScript as a convenient way to declare variables that can contain "one of" a bunch of keywords. For example, the actor class contains the enumeration EPhysics which describes the physics which Unreal should apply to the actor. This can be set to one of the predefined values like PHYS_None, PHYS_Walking, PHYS_Falling, and so on.

Internally, enumerations are stored as byte variables. In designing UnrealScript, enumerations were not seen as a necessity, but it makes code so much easier to read to see that an actor’s physics mode is being set to "PHYS_Swimming" than (for example) "3".

Here is sample code that declares enumerations.

// Declare the EColor enumeration, with three values.
enum EColor
{
    CO_Red,
    CO_Green,
    CO_Blue
};

// Now, declare two variables of type EColor.
var EColor ShirtColor, HatColor;

// Alternatively, you can declare variables and
// enumerations together like this:
var enum EFruit
{
    FRUIT_Apple,
    FRUIT_Orange,
    FRUIT_Bannana
} FirstFruit, SecondFruit;

In the Unreal source, we always declare enumeration values like LT_Steady, PHYS_Falling, and so on, rather than as simply "Steady" or "Falling". This is just a matter of programming style, and is not a requirement of the language.

UnrealScript only recognizes unqualified enum tags (like FRUIT_Apple) in classes where the enumeration was defined, and in its subclasses. If you need to refer to an enumeration tag defined somewhere else in the class hierarchy, you must "qualify it":

FRUIT_Apple // If Unreal can't find this enum tag...
EFruit.FRUIT_Apple // Then qualify it like this.

Structs

An UnrealScript struct is a way of cramming a bunch of variables together into a new kind of super-variable called a struct. UnrealScript structs are just like C structs, in that they can contain any simple variables or arrays.

You can declare a struct as follows:

// A point or direction vector in 3D space.
struct Vector
{
    var float X;
    var float Y;
    var float Z
};

Once you declare a struct, you are ready to start declaring specific variables of that struct type:

// Declare a bunch of variables of type Vector.
var Vector Position;
var Vector Destination;

To access a component of a struct, use code like the following.

function MyFunction()
{
    Local Vector A, B, C;

    // Add some vectors.
    C = A + B;

    // Add just the x components of the vectors.
    C.X = A.X + B.X;

    // Pass vector C to a function.
    SomeFunction( C );

    // Pass certain vector components to a function.
    OtherFunction( A.X, C.Z );
}

You can do anything with Struct variables that you can do with other variables: you can assign variables to them, you can pass them to functions, and you can access their components.

There are several Structs defined in the Object class which are used throughout Unreal. You should become familiar with their operation, as they are fundamental building blocks of scripts:

  • Vector: A unique 3D point or vector in space, with an X, Y, and Z component.
  • Plane: Defines a unique plane in 3D space. A plane is defined by its X, Y, and Z components (which are assumed to be normalized) plus its W component, which represents the distance of the plane from the origin, along the plane’s normal (which is the shortest line from the plane to the origin).
  • Rotation: A rotation defining a unique orthogonal coordinate system. A rotation contains Pitch, Yaw, and Roll components.
  • Coords: An arbitrary coordinate system in 3D space.
  • Color: An RGB color value.
  • Region: Defines a unique convex region within a level.

Expressions

Constants

In UnrealScript, you can specify constant values of nearly all data types:

  • Integer and byte constants are specified with simple numbers, for example: 123
  • If you must specify an integer or byte constant in hexidecimal format, use i.e.: 0x123
  • Floating point constants are specified with decimal numbers like: 456.789
  • String constants must be enclosed in double quotes, for example: "MyString"
  • Name constants must be enclosed in single quotes, for example ‘MyName’
  • Vector constants contain X, Y, and Z values like this: Vect(1.0,2.0,4.0)
  • Rotation constants contant Pitch, Yaw, and Roll values like this: Rot(0x8000,0x4000,0)
  • The "None" constant refers to "no object" (or equivalantly, "no actor").
  • The "Self" constant refers to "this object" (or equivalantly, "this actor"), i.e. the object whose script is executing.
  • General object constants are specified by the object type followed by the object name in single quotes, for example: texture ‘Default’
  • EnumCount gives you the number of elements in an enumeration, for example: EnumCount(ELightType)
  • ArrayCount gives you the number of elements in an array, for example: ArrayCount(Touching)

You can use the "const" keyword to declare constants which you can later refer to by name. For example:

const LargeNumber=123456;
const PI=3.14159;
const MyName="Tim";
const Northeast=Vect(1.0,1.0,0.0);

Constants can be defined within classes or within structs.

To access a constant which was declared in another class, use the "classname.constname" syntax, for example:

Pawn.LargeNumber

Expressions

To assign a value to a variable, use "=" like this:

function Test()
{
    local int i;
    local string[80] s;
    local vector v, q;

    i = 10; // Assign a value to integer variable i.
    s = "Hello!"; // Assign a value to string variable s.
    v = q; // Copy value of vector q to v.
}

In UnrealScript, whenever a function or other expression requires a certain type of data (for example, an "float"), and you specify a different type of data (for example, an "int), the compiler will try to convert the value you give to the proper type. Conversions among all the numerical data types (byte, int, and float) happen automatically, without any work on your part.

UnrealScript is also able to many other built-in data types to other types, if you explicitly convert them in code. The syntax for this is:

function Test()
{
    local int i;
    local string[80] s;
    local vector v, q;
    local rotation r;

    s = string(i); // Convert integer i to a string, and assign it to s.
    s = string(v); // Convert vector v to a string, and assign it to s.
    v = q + vector(r); // Convert rotation r to a vector, and add q.
}

Here is the complete set of non-automatic conversions you can use in UnrealScript:

  • String to Byte, Int, Float: Tries to convert a string like "123" to a value like 123. If the string doesn’t represent a value, the result is 0.
  • Byte, Int, Float, Vector, Rotation to String: Converts the number to its textual representation.
  • String to Vector, Rotation: Tries to parse the vector or rotation’s textual representation.
  • String to Bool: Converts the case-insensitive words "True" or "False" to True and False; converts any non-zero value to True; everything else is False.
  • Bool to String: Result is either "True" or "False".
  • Byte, Int, Float, Vector, Rotation to Bool: Converts nonzero values to True; zero values to False.
  • Bool to Byte, Int, Float: Converts True to 1; False to 0.
  • Name to String: Converts the name to the text equivalant.
  • Rotation to Vector: Returns a vector facing "forward" according to the rotation.
  • Vector to Rotation: Returns a rotation pitching and yawing in the direction of the vector; roll is zero.
  • Object (or Actor) to Int: Returns an integer that is guaranteed unique for that object.
  • Object (or Actor) to Bool: Returns False if the object is None; False otherwise.
  • Object (or Actor) to String: Returns a textual representation of the object.

Converting object references among classes

Just like the conversion functions above, which convert among simple datatypes, in UnrealScript you can convert actor and object references among various types. For example, all actors have a variable named "Target", which is a reference to another actor. Say you are writing a script where you need to check and see if your Target belongs to the "Pawn" actor class, and you need to do something special with your target that only makes sense when it’s a pawn -- for example, you need to call one of the Pawn functions. The actor cast operators let you do this. Here’s an example:

var actor Target;
//...

function TestActorConversions()
{
    local Pawn P;

    // See if my target is a Pawn.
    P = Pawn(Target);
    if( P != None )
    {
        // Target is a pawn, so set its Enemy to Self.
        P.Enemy = Self;
    }
    else
    {
        // Target is not a pawn.
    }
}

To perform an actor conversion, type the class name followed by the actor expression you wish to convert, in parenthesis. Such a conversion will either succeed or fail based on whether the conversion is sensible. In the above example, if your Target is referencing a Trigger object rather than a pawn, the expression Pawn(Target) will return "None", since a Trigger can’t be converted to a Pawn. However, if your Target is referencing a Brute object, the conversion will successfully return the Brute, because Brute is a subclass of Pawn.

Thus, actor conversions have two purposes: First, you can use them to see if a certain actor reference belongs to a certain class. Second, you can use them to convert an actor reference from one class to a more specific class. Note that these conversions don’t affect the actor you’re converting at all -- they just enable UnrealScript to treat the actor reference as if it were a more specific type.

Another example of conversions lies in the Inventory script. Each Inventory actor is owned by a Pawn, even though its Owner variable can refer to any Actor. So a common theme in the Inventory code is to cast Owner to a Pawn, for example:

// Called by engine when destroyed.
function Destroyed()
{
    // Remove from owner's inventory.
    if( Pawn(Owner)!=None )
        Pawn(Owner).DeleteInventory( Self );
}

Functions

Declaring Functions

In UnrealScript, you can declare new functions and write new versions of existing functions. Functions can take one or more parameters (of any variable type UnrealScript supports), and can optionally return a value. Though most functions are written directly in UnrealScript, you can also declare functions that can be called from UnrealScript, but which are implemented in C++ and reside in a DLL. The Unreal technology supports all possible combinations of function calling: The C++ engine can call script functions; script can call C++ functions; and script can call script.

Here is a simple function declaration. This function takes a vector as a parameter, and returns a floating point number:

// Function to compute the size of a vector.
function float VectorSize( vector V )
{
    return sqrt( V.X * V.X + V.Y * V.Y + V.Z * V.Z );
}

The word "function" always precedes a function declaration. It is followed by the optional return type of the function (in this case, "float"), then the function name, and then the list of function parameters enclosed in parenthesis.

When a function is called, the code within the brackets is executed. Inside the function, you can declare local variables (using the "local" keyword"), and execute any UnrealScript code. The optional "return" keyword causes the function to immediately return a value.

You can pass any UnrealScript types to a function (including arrays), and a function can return any type.

By default, any local variables you declare in a function are initialized to zero.

Function calls can be recursive. For example, the following function computes the factorial of a number:

// Function to compute the factorial of a number.
function int Factorial( int Number )
{
    if( Number <= 0 )
        return 1;
    else
        return Number * Factorial( Number – 1 );
}

Some UnrealScript functions are called by the engine whenever certain events occur. For example, when an actor is touched by another actor, the engine calls its "Touch" function to tell it who is touching it. By writing a custom "Touch" function, you can take special actions as a result of the touch occuring:

// Called when something touches this actor.
function Touch( actor Other )
{
    Log( "I was touched!")
    Other.Message( "You touched me!" );
}

The above function illustrates several things. First of all, the function writes a message to the log file using the "Log" command (which is the equivalent of Basic’s "print" command and C’s "printf"). Second, it calls the "Message" function residing in the actor Other. Calling functions in other actors is a common action in UnrealScript, and in object-oriented languages like Java in general, because it provides a simple means for actors to communicate with each other.

Function parameter specifiers

When you normally call a function, UnrealScript makes a local copy of the parameters you pass the function. If the function modifies some of the parameters, those don’t have any effect on the variables you passed in. For example, the following program:

function int DoSomething( int x )
{
    x = x * 2;
    return x;
}
function int DoSomethingElse()
{
    local int a, b;

    a = 2;
    log( "The value of a is " $ a );

    b = DoSomething( a );
    log( "The value of a is " $ a );
    log( "The value of b is " $ b );
}

Produces the following output when DoSomethingElse is called:

The value of a is 2
The value of a is 2
The value of b is 4

In other words, the function DoSomething was futzing with a local copy of the variable "a" which was passed to it, and it was not affecting the real variable "a".

The "out" specified lets you tell a function that it should actually modify the variable that is passed to it, rather than making a local copy. This is useful, for example, if you have a function that needs to return several values to the caller. You can juse have the caller pass several variables to the function which are "out" values. For example:

// Compute the minimum and maximum components of a vector.
function VectorRange( vector V, out float Min, out float Max )
{
    // Compute the minimum value.
    if ( V.X<V.Y && V.X<V.Z ) Min = V.X;
    else if( V.Y<V.Z ) Min = V.Y;
    else Min = V.Z;

    // Compute the maximum value.
    if ( V.X>V.Y && V.X>V.Z ) Max = V.X;
    else if( V.Y>V.Z ) Max = V.Y;
    else Max = V.Z;
}

Without the "out" keyword, it would be painful to try to write functions that had to return more than one value.

With the "optional" keyword, you can make certain function parameters optional, as a convenience to the caller. For UnrealScript functions, optional parameters which the caller doesn’t specify are set to zero. For native functions, the default values of optional parameters depends on the function. For example, the Spawn function takes an optional location and rotation, which default to the spawning actor’s location and rotation.

The "coerce" keyword forces the caller’s parameters to be converted to the specified type (even if UnrealScript normally would not perform the conversion automatically). This is useful for functions that deal with strings, so that the parameters are automatically converted to strings for you.

Function overriding

"Function overriding" refers to writing a new version of a function in a subclass. For example, say you’re writing a script for a new kind of monster called a Demon. The Demon class, which you just created, expands the Pawn class. Now, when a pawn sees a player for the first time, the pawn’s "SeePlayer" function is called, so that the pawn can start attacking the player. This is a nice concept, but say you wanted to handle "SeePlayer" differently in your new Demon class. How do you do this? Function overriding is the answer.

To override a function, just cut and paste the function definition from the parent class into your new class. For example, for SeePlayer, you could add this to your Demon class.

// New Demon class version of the Touch function.
function SeePlayer( actor SeenPlayer )
{
    log( "The demon saw a player" );
    // Add new custom functionality here…
}

Function overriding is the key to creating new UnrealScript classes efficiently. You can create a new class that expands on an existing class. Then, all you need to do is override the functions which you want to be handled differently. This enables you to create new kinds of objects without writing gigantic amounts of code.

Several functions in UnrealScript are declared as "final". The "final" keyword (which appears immediately before the word "function") says "this function cannot be overridden by child classes". This should be used in functions which you know nobody would want to override, because it results in faster script code. For example, say you have a "VectorSize" function that computes the size of a vector. There’s absolutely no reason anyone would ever override that, so declare it as "final". On the other hand, a function like "Touch" is very context-dependent and should not be final.

Advanced function specifiers

Static: A static function acts like a C global function, in that it can be called without having a reference to an object of the class. Static functions can call other static functions, and can access the default values of variables. Static functions cannot call non-static functions and they cannot access instance variables (since they are not executed with respect to an instance of an object). Unlike languages like C++, static functions are virtual and can be overridden in child classes. This is useful in cases where you wish to call a static function in a variable class (a class not known at compile time, but referred to by a variable or an expression).

Singular: The "singular" keyword, which appears immediately before a function declaration, prevents a function from calling itself recursively. The rule is this: If a certain actor is already in the middle of a singular function, any subsequent calls to singular functions will be skipped over. This is useful in avoiding infinite-recursive bugs in some cases. For example, if you try to move an actor inside of your "Bump" function, there is a good chance that the actor will bump into another actor during its move, resulting in another call to the "Bump" function, and so on. You should be very careful in avoiding such behaviour, but if you can’t write code with complete confidence that you’re avoiding such potential recursive situations, use the "singular" keyword.

Native: You can declare UnrealScript functions as "native", which means that the function is callable from UnrealScript, but is actually written (elsewhere) in C++. For example, the Actor class contains a lot of native function definitions, such as:

native(266) final function bool Move( vector Delta );

The number inside the parenthesis after the "native" keyword corresponds to the number of the function as it was declared in C++ (using the AUTOREGISTER_NATIVE macro). The native function is expected to reside in the DLL named identically to the package of the class containing the UnrealScript definition.

Latent: Declares that an native function is latent, meaning that it can only be called from state code, and it may return after some game-time has passed.

Iterator: Declares that an native function is an iterator, which can be used to loop through a list of actors using the "foreach" command.

Simulated: Declares that a function may execute on the client-side when an actor is either a simulated proxy or an autonomous proxy. All functions that are both native and final are automatically simulated as well.

Operator, PreOperator, PostOperator: These keywords are for declaring a special kind of function called an operator (equivalent to C++ operators). This is how UnrealScript knows about all of the built-in operators like "+", "-", "==", and "||". I'm not going into detail on how operators work in this document, but the concept of operators is similar to C++, and you can declare new operator functions and keywords as UnrealScript functions or native functions.

Event: The "event" keyword has the same meaning to UnrealScript as "function". However, when you export a C++ header file from Unreal using "unreal -make -h", UnrealEd automatically generates a C++ -> UnrealScript calling stub for each "event". This is a much cleaner replacement for the old "PMessageParms" struct, because it automatically keeps C++ code synched up with UnrealScript functions and eliminates the possibility of passing invalid parameters to an UnrealScript function. For example, this bit of UnrealScript code:

event Touch( Actor Other )
{ ... }

Generates this piece of code in EngineClasses.h:

void Touch(class AActor* Other)
{
    FName N("Touch",FNAME_Intrinsic);
    struct {class AActor* Other; } Parms;
    Parms.Other=Other;
    ProcessEvent(N,&Parms);
}

Thus enabling you to call the UnrealScript function from C++ like this:

AActor *SomeActor, *OtherActor;
Actor->Touch(OtherActor);

Program Structure

UnrealScript supports all the standard flow-control statements of C/C++/Java:

For Loops

"For" loops let you cycle through a loop as long as some condition is met. For example:

// Example of "for" loop.
function ForExample()
{
    local int i;
    log( "Demonstrating the for loop" );
    for( i=0; i<4; i++ )
    {
        log( "The value of i is " $ i );
    }
    log( "Completed with i=" $ i);
}

The output of this loop is:

Demonstrating the for loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4

In a for loop, you must specify three expressions separated by semicolons. The first expression is for initializing a variable to its starting value. The second expression gives a condition which is checked before each iteration of the loop executes; if this expression is true, the loop executes. If it’s false, the loop terminates. The third condition gives an expression which increments the loop counter.

Though most "for" loop expressions just update a counter, you can also use "for" loops for more advanced things like traversing linked lists, by using the appropriate initialization, termination, and increment expressions.

In all of the flow control statements, you can either execute a single statement, without brackets, as follows:

for( i=0; i<4; i++ )
    log( "The value of i is " $ i );

Or you can execute multiple statements, surrounded by brackets, like this:

for( i=0; i<4; i++ )
{
    log( "The value of i is" );
    log( i );
}

Do-While Loops

"Do"-"Until" loops let you cycle through a loop while some ending expression is true. Note that Unreal's do-until syntax differs from C/Java (which use do-while).

// Example of "do" loop.
function DoExample()
{
    local int i;
    log( "Demonstrating the do loop" );
    do
    {
        log( "The value of i is " $ i );
        i = i + 1;
    } until( i == 4 );
    log( "Completed with i=" $ i);
}

The output of this loop is:

Demonstrating the do loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4

While Loops

"While" loops let you cycle through a loop while some starting expression is true.

// Example of "while" loop.
function WhileExample()
{
    local int i;
    log( "Demonstrating the while loop" );
    while( i < 4 )
    {
        log( "The value of i is " $ i );
        i = i + 1;
    }
    log( "Completed with i=" $ i);
}

The output of this loop is:

Demonstrating the do loop
The value of i is 0
The value of i is 1
The value of i is 2
The value of i is 3
Completed with i=4

Break

The "break" command exits out of the nearest loop ("For", "Do", or "While").

// Example of "while" loop.
function WhileExample()
{
    local int i;
    log( "Demonstrating break" );
    for( i=0; i<10; i++ )
    {
        if( i == 3 )
            break;
        log( "The value of i is " $ i );
    }
    log( "Completed with i=" $ i );
}

The output of this loop is:

Demonstrating break
The value of i is 0
The value of i is 1
The value of i is 2
Completed with i=3

Goto

The "Goto" command goes to a label somewhere in the current function or state.

// Example of "goto".
function GotoExample()
{
    log( "Starting GotoExample" );
    goto Hither;
Yon:
    log( "At Yon" );
    goto Elsewhere;
Hither:
    log( "At Hither" );
    goto Yon;
Elsewhere:
    log( "At Elsewhere" );
}

The output is:

Starting GotoExample
At Hither
At Yon
At Elsewhere

Conditional Statements

"If", "Else If", and "Else" let you execute code if certain conditions are met.

// Example of simple "if".
if( LightBrightness < 20 )
    log( "My light is dim" );

// Example of "if-else".
if( LightBrightness < 20 )
    log( "My light is dim" );
else
    log( "My light is bright" );

// Example if "if-else if-else".
if( LightBrightness < 20 )
    log( ‘My light is dim" );
else if( LightBrightness < 40 )
    log( "My light is medium" );
else if( LightBrightness < 60 )
    log( "My light is kinda bright" );
else
    log( "My light is very bright" );

// Example if "if" with brackets.
if( LightType == LT_Steady )
{
    log( "Light is steady" );
}
else
{
    log( "Light is not steady" );
}

Case Statements

"Switch", "Case", "Default", and "Break" let you handle lists of conditions easily.

// Example of switch-case.
function TestSwitch()
{
    // Executed one of the case statements below, based on
    // the value in LightType.
    switch( LightType )
    {
        case LT_None:
            log( "There is no lighting" );
            break;
        case LT_Steady:
            log( "There is steady lighting" );
            break;
        case LT_Backdrop:
            log( "There is backdrop lighting" );
            break;
        default:
            log( "There is dynamic" );
            break;
    }
}

A "switch" statement consists of one or more "case" statements, and an optional "default" statement. After a switch statement, execution goes to the matching "case" statement if there is one; otherwise execution goes to the "default" statement; otherwise execution continues past the end of the "select" statement.

After you write code following a "case" label, you must use a "break" statement to cause execution to go past the end of the "switch" statement. If you don’t use a "break", execution "falls through" to the next "case" handler.

States

Overview of States

Historically, game programmers have been using the concept of states ever since games evolved past the "pong" phase. States (and what is known as "state machine programming") are a natural way of making complex object behaviour manageable. However, before UnrealScript, states have not been supported at the language level, requiring developers to create C/C++ "switch" statements based on the object’s state. Such code was difficult to write and update.

UnrealScript supports states at the language level.

In UnrealScript, each actor in the world is always in one and only one state. Its state reflects the action it wants to perform. For example, moving brushes have several states like "StandOpenTimed" and "BumpOpenTimed". Pawns have several states such as "Dying", "Attacking", and "Wandering".

In UnrealScript, you can write functions and code which exist in a particular state. These functions are only called when the actor is in that state. For example, say you’re writing a monster script, and you’re contemplating how to handle the "SeePlayer" function. When you’re wandering around, you want to attack the player you see. When you’re already attacking the player, you want to continue on uninterrupted.

The easiest way to do this is by defining several states (Wandering and Attacking), and writing a different version of "Touch" in each state. UnrealScript supports this.

Before delving deeper into states, you need to understand that there are two major benefits to states, and one complication:

  • Benefit: States provide a simple way to write state-specific functions, so that you can handle the same function in different ways, depending on what the actor is doing.
  • Benefit: With a state, you can write special "state code", using all of the regular UnrealScript commands plus several special functions known as "latent functions". A latent function is a function which executes "slowly", and may return after a certain amount of "game time" has passed. This enables you to perform time-based programming – a major benefit which neither C, C++, nor Java offer. Namely, you can write code in the same way you conceptualize it; for example, you can write a script that says the equivalent of "open this door; pause 2 seconds; play this sound effect; open that door; release that monster and have it attack the player". You can do this with simple, linear code, and the Unreal engine takes care of the details of managing the time-based execution of the code.
  • Complication: Now that you can have functions (like "Touch") overridden in multiple states as well as in child classes, you have the burden of figuring out exactly which "Touch" function is going to be called in a specific situation. UnrealScript provides rules which clearly delineate this process, but it is something you must be aware of if you create complex hierarchies of classes and states.

Here is an example of states from the TriggerLight script:

// Trigger turns the light on.
state() TriggerTurnsOn
{
    function Trigger( actor Other, pawn EventInstigator )
    {
        Trigger = None;
        Direction = 1.0;
        Enable( 'Tick' );
    }
}

// Trigger turns the light off.
state() TriggerTurnsOff
{
    function Trigger( actor Other, pawn EventInstigator )
    {
        Trigger = None;
        Direction = -1.0;
        Enable( 'Tick' );
    }
}

Here you are declaring two different states (TriggerTurnsOn and TriggerTurnsOff), and you’re writing a different version of the Trigger function in each state. Though you could pull off this implementation without states, using states makes the code far more modular and expandable: in UnrealScript, you can easily subclass an existing class, add new states, and add new functions. If you had tried to do this without states, the resulting code would be more difficult to expand later.

A state can be declared as editable, meaning that the user can set an actor’s state in UnrealEd, or not. To declare an editable state, do the following:

state() MyState
{
    //...
}

To declare a non-editable state, do this:

state MyState
{
    //...
}

You can also specify the automatic, or initial state that an actor should be in by using the "auto" keyword. This causes all new actors to be placed in that state when they first are activated:

auto state MyState
{
    //...
}

State Labels and Latent Functions

In addition to functions, a state can contain one or more labels followed by UnrealScript code. For example:

auto state MyState
{
Begin:
    Log( "MyState has just begun!" );
    Sleep( 2.0 );
    Log( "MyState has finished sleeping" );
    goto Begin;
}

The above state code prints the message "MyState has just begun!", then it pauses for two seconds, then it prints the message "MyState has finished sleeping". The interesting thing in this example is the call to the latent function "Sleep": this function call doesn’t return immediately, but returns after a certain amount of game time has passed. Latent functions can only be called from within state code, and not from within functions. Latent functions let you manage complex chains of events which include the passage of time.

All state code begins with a label definition; in the above example the label is named "Begin". The label provides a convenient entry point into the state code. You can use any label name in state code, but the "Begin" label is special: it is the default starting point for code in that state.

There are three main latent functions available to all actors:

  • Sleep( float Seconds ) pauses the state execution for a certain amount of time, and then continues.
  • FinishAnim() waits until the current animation sequence you’re playing completes, and then continues. This function makes it easy to write animation-driven scripts, scripts whose execution is governed by mesh animations. For example, most of the AI scripts are animation-driven (as opposed to time-driven), because smooth animation is a key goal of the AI system.
  • FinishInterpolation() waits for the current InterpolationPoint movement to complete, and then continues.

The Pawn class defines several important latent functions for actions such as navigating through the world and short-term movement. See the separate AI docs for descriptions of their usage.

Three native UnrealScript functions are particularly useful when writing state code:

  • The "Goto" function (similar to the C/C++/Basic goto) within a state causes the state code to continue executing at a different label.
  • The special Goto('') command within a state causes the state code execution to stop. State code execution doesn’t continue until you go to a new state, or go to a new label within the current state.
  • The "GotoState" function causes the actor to go to a new state, and optionally continue at a specified label (if you don’t specify a label, the default is the "Begin" label). You can call GotoState from within state code, and it goes to the destination immediately. You can also call GotoState from within any function in the actor, but that does not take effect immediately: it doesn’t take effect until execution returns back to the state code.

Here is an example of the state concepts discussed so far:

// This is the automatic state to execute.
auto state Idle
{
    // When touched by another actor…
    function Touch( actor Other )
    {
        log( "I was touched, so I’m going to Attacking" );
        GotoState( ‘Attacking’ );
        Log( "I have gone to the Attacking state" );
    }
Begin:
    log( "I am idle…" );
    sleep( 10 );
    goto ‘Begin’;
}

// Attacking state.
state Attacking
{
Begin:
    Log( "I am executing the attacking state code" );
    //...
}

When you run this program and then go touch the actor, you will see:

I am idle...
I am idle...
I am idle...
I was touched, so I’m going to Attacking
I have gone to the Attacking state
I am executing the attacking state code

Make sure you understand this important aspect of GotoState: When you call GotoState from within a function, it does not go to the destination immediately, rather it goes there once execution returns back to the state code.

State inheretance and scoping rules

In UnrealScript, when you subclass an existing class, your new class inherets all of the variables, functions and states from its parent class. This is well-understood.

However, the addition of the state abstraction to the UnrealScript programming model adds additional twists to the inheretance and scoping rules. The complete inheretance rules are:

  • A new class inherets all of the variables from its parent class.
  • A new class inherents all of its parent class’s non-state functions. You can override any of those inhereted non-state functions. You can add entirely new non-state functions.
  • A new class inherets all of its parent class’s states, including the functions and labels within those states. You can override any of the inhereted state functions, and you can override any of the inhereted state labels, you can add new state functions, and you can add new state labels.

Here is an example of all the overriding rules:

// Here is an example parent class.
class MyParentClass expands Actor;

// A non-state function.
function MyInstanceFunction()
{
    log( "Executing MyInstanceFunction" );
}

// A state.
state MyState
{
    // A state function.
    function MyStateFunction()
    {
        Log( "Executing MyStateFunction" );
    }
// The "Begin" label.
Begin:
    Log("Beginning MyState");
}

// Here is an example child class.
class MyChildClass expands MyParentClass;

// Here I’m overriding a non-state function.
function MyInstanceFunction()
{
    Log( "Executing MyInstanceFunction in child class" );
}

// Here I’m redeclaring MyState so that I can override MyStateFunction.
state MyState
{
    // Here I’m overriding MyStateFunction.
    function MyStateFunction()
    {
        Log( "Executing MyStateFunction" );
    }
// Here I’m overriding the "Begin" label.
Begin:
    Log( "Beginning MyState in MyChildClass" );
}

When you have a function that is implemented globally, in one or more states, and in one or more parent classes, you need to understand which version of the function will be called in a given context. The scoping rules, which resolves these complex situations, are:

  • If the object is in a state, and an implementation of the function exists somewhere in that state (either in the actor’s class or in some parent class), the most-derived state version of the function is called.
  • Otherwise, the most-derived non-state version of the function is called.

Advanced state programming

If a state doesn’t override a state of the same name in the parent class, then you can optionally use the "expands" keyword to make the state expand on an existing state in the current class. This is useful, for example, in a situatio where you have a group of similar states (such as MeleeAttacking and RangeAttacking) which have a lot of functionality in common. In this case you could declare a base Attacking state as follows:

// Base Attacking state.
state Attacking
{
    // Stick base functions here...
}

// Attacking up-close.
state MeleeAttacking expands Attacking
{
    // Stick specialized functions here...
}

// Attacking from a distance.
state RangeAttacking expands Attacking
{
    // Stick specialized functions here...
}

A state can optionally use the "ignores" specifier to ignore functions while in a state. The syntax for this is:

// Declare a state.
state Retreating
{
    // Ignore the following messages...
    ignores Touch, UnTouch, MyFunction;

    // Stick functions here...
}

You can tell what specific state an actor is in from its "state" variable, a variable of type "name".

It is possible for an actor to be in "no state" by using GotoState(‘’). When an actor is in "no state", only its global (non-state) functions are called.

Whever you use the GotoState command to set an actor’s state, the engine can call two special notification functions, if you have defined them: EndState() and BeginState(). EndState is called in the current state immediately before the new state is begun, and BeginState is called immediately after the new state begins. These functions provide a convenient place to do any state-specific initialization and cleanup which your state may require.

Language Functionality

Built-in operators and their precedence

UnrealScript provides a wide variety of C/C++/Java-style operators for such operations as adding numbers together, comaring values, and incrementing variables. The complete set of operators is defined in Object.u, but here is a recap. Here are the standard operators, in order of precedence. Note that all of the C style operators have the same precedence as they do in C.

Operator | Types it applies to | Meaning
$ | string | String concatenation
*= | byte, int, float, vector, rotation | Multiply and assign
/= | byte, int, float, vector, rotation | Divide and assign
+= | byte, int, float, vector | Add and assign
-= | byte, int, float, vector | Subtract and assign
|| | bool | Logical or
&& | bool | Logical and
& | int | Bitwise and
| | int | Bitwise or
^ | int | Bitwise exlusive or
!= | All | Compare for inequality
== | All | Compare for equality
< | byte, int, float, string | Less than
> | byte, int, float, string | Greater than
<= | byte, int, float, string | Less than or equal to
>= | byte, int, float, string | Greater than or equal to
~= | float, string | Approximate equality (within 0.0001), case-insensitive equality.
<< | int, vector | Left shift (int), Forward vector transformation (vector)
>> | int, vector | Right shift (int), Reverse vector transformation (vector)
+ | byte, int, float, vector | Add
- | byte, int, float, vector | Subtract
% | float | Modulo (remainder after division)
* | byte, int, float, vector, rotation | Multiply
/ | byte, int, float, vector, rotation | Divide
Dot | vector | Vector dot product
Cross | vector | Vector cross product
** | float | Exponentiation

The above table lists the operators in order of precedence (with operators of the same precedence grouped together). When you type in a complex expression like "1*2+3*4", UnrealScript automatically groups the operators by precedence. Since multiplication has a higher precedence than addition, the expression is evaluated as "(1*2)+(3*4)".

The "&&" (logical and) and "||" (logical or) operators are short-circuited: if the result of the expression can be determined solely from the first expression (for example, if the first argument of && is false), the second expression is not evaluated.

In addition, UnrealScript supports the following unary operators:

  • ! (bool) Logical not.
  • - (int, float) negation.
  • ~ (int) bitwise negation.
  • ++, -- Decrement (either before or after a variable).

General purpose functions

Integer functions:

  • int Rand( int Max ); Returns a random number from 0 to Max-1.
  • int Min( int A, int B ); Returns the minimum of the two numbers.
  • int Max( int A, int B ); Returns the maximum of the two numbers.
  • int Clamp( int V, int A, int B ); Returns the first number clamped to the interval from A to B.

Floating point functions:

  • float Abs( float A ); Returns the absolute value of the number.
  • float Sin( float A ); Returns the sine of the number expressed in radius.
  • float Cos( float A ); Returns the cosine of the number expressed in radians.
  • float Tan( float A ); Returns the tangent of the number expressed in radians.
  • float Atan( float A ); Returns the inverse tangent of the number expressed in radians.
  • float Exp( float A ); Returns the constant "e" raised to the power of A.
  • float Loge( float A ); Returns the log (to the base "e") of A.
  • float Sqrt( float A ); Returns the square root of A.
  • float Square( float A ); Returns the square of A = A*A.
  • float FRand(); Returns a random number from 0.0 to 1.0.
  • float FMin( float A, float B ); Returns the minimum of two numbers.
  • float FMax( float A, float B ); Returns the maximum of two numbers.
  • float FClamp( float V, float A, float B ); Returns the first number clamped to the interval from A to B.
  • float Lerp( float Alpha, float A, float B ); Returns the linear interpolation between A and B.
  • float Smerp( float Alpha, float A, float B ); Returns an Alpha-smooth nonlinear interpolation between A and B.

Unreal’s string functions have a distinct Basic look and feel:

  • int Len( coerce string[255] S ); Returns the length of a string.
  • int InStr( coerce string[255] S, coerce string[255] t); Returns the offset into the first string of the second string if it exists, or -1 if not.
  • string[255] Mid ( coerce string[255] S, int i, optional int j ); Returns the middle part of the string S, starting and character i and including j characters (or all of them if j is not specified).
  • string[255] Left ( coerce string[255] S, int i ); Returns the i leftmost characters of s.
  • string[255] Right ( coerce string[255] S, int i ); Returns the i rightmost characters of s.
  • string[255] Caps ( coerce string[255] S ); Returns S converted to uppercase.

Vector functions:

  • float Size( vector A ); Returns the euclidean size of the vector (the square root of the sum of the components squared).
  • vector Normal( vector A ); Returns a vector of size 1.0, facing in the direction of the specified vector.
  • Invert ( out vector X, out vector Y, out vector Z ); Inverts a coordinate system specified by three axis vectors.
  • vector VRand ( ); Returns a uniformly distributed random vector.
  • float Dist ( vector A, vector B ); Returns the euclidean distance between two points.
  • vector MirrorVectorByNormal( vector Vect, vector Normal ); Mirrors a vector about a specified normal vector.

Advanced Language Features

ForEach and iterator functions

UnrealScript’s "foreach" command makes it easy to deal with large groups of actors, for example all of the actors in a level, or all of the actors within a certain distance of another actor. "foreach" works in conjunction with a special kind of function called an "iterator" function whose purpose is to iterate through a list of actors.

Here is a simple example of foreach:

// Display a list of all lights in the level.
function Something()
{
    local actor A;

    // Go through all actors in the level.
    log( "Lights:" );
    foreach AllActors( class ‘Actor’, A )
    {
        if( A.LightType != LT_None )
            log( A );
    }
}

The first parameter in all "foreach" commands is a constant class, which specifies what kinds of actors to search. You can use this to limit the search to, for example, all Pawns only.

The second parameter in all "foreach" commands is a variable which is assigned an actor on each iteration through the "foreach" loop.

Here are all of the iterator functions which work with "foreach".

  1. AllActors ( class BaseClass, out actor Actor, optional name MatchTag ); Iterates through all actors in the level. If you specify an optional MatchTag, only includes actors which have a "Tag" variable matching the tag you specified.
  2. ChildActors( class BaseClass, out actor Actor ); Iterates through all actors owned by this actor.
  3. BasedActors( class BaseClass, out actor Actor ); Iterates throgh all actors which are standing on this actor.
  4. TouchingActors( class BaseClass, out actor Actor ); Iterates through all actors which are touching (interpenetrating) this actor.
  5. TraceActors( class BaseClass, out actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent ); Iterates through all actors which touch a line traced from the Start point to the End point, using a box of collision extent Extent. On each iteration, HitLoc is set to the hit location, and HitNorm is set to an outward-pointing hit normal.
  6. RadiusActors( class BaseClass, out actor Actor, float Radius, optional vector Loc ); Iterates through all actors within a specified radius of the specified location (or if none is specified, this actor’s location).
  7. VisibleActors( class BaseClass, out actor Actor, optional float Radius, optional vector Loc ); Iterates through a list of all actors who are visible to the specified location (or if no location is specified, this actor’s location).

Function Calling Specifiers

In complex programming situations, you will often need to call a specific version of a function, rather than the one that’s in the current scope. To deal with these cases, UnrealScript provides the following keywords:

  • Global: Calls the most-derived global (non-state) version of the function.
  • Super: Calls the corresponding version of the function in the parent class. The function called may either be a state or non-state function depending on context.
  • Super(classname): Calls the corresponding version of the function residing in (or above) the specified class. The function called may either be a state or non-state function depending on context.

It is not valid to combine multiple calling specifiers (i.e. Super(Actor).Global.Touch).

Here are some examples of calling specifiers:

class MyClass expands Pawn;

function MyExample( actor Other )
{
    Super(Pawn).Touch( Other );
    Global.Touch( Other );
    Super.Touch( Other );
}

As an additional example, the BeginPlay() function is called when an actor is about to enter into gameplay. The BeginPlay() function is implemented in the Actor class and it contains some important functionality that needs to be executed. Now, say you want to override BeginPlay() in your new class MyClass, to add some new functionality. To do that safely, you need to call the version of BeginPlay() in the parent class:

class MyClass expands Pawn;

function BeginPlay()
{
    // Call the version of BeginPlay in the parent class (important).
    Super.BeginPlay();

    // Now do custom BeginPlay stuff.
    //...
}

Default values of variables

Accessing default values of variables

UnrealEd enables level designers to edit the "default" variables of an object's class. When a new actor is spawned of the class, all of its variables are initialized to those defaults. Sometimes, it’s useful to manually reset a variable to its default value. For example, when the player drops an inventory item, the inventory code needs to reset some of the actor’s values to its defaults. In UnrealScript, you can access the default variables of a class with the "Default." keyword. For example:

var() float Health, Stamina;
//...

// Reset some variables to their defaults.
function ResetToDefaults()
{
    // Reset health, and stamina.
    Health = Default.Health;
    Stamina = Default.Stamina;
}

Accessing default values of variables in a variable class

If you have a class reference (a variable of "class" or "class<classlimitor>" type), you can access the default properties of the class it references, without having an object of that class. This syntax works with any expression that evaluates to class type.

var class C;
var class<Pawn> PC;
Health = class'Spotlight'.default.LightBrightness; // Access the default value of LightBrightness in the Spotlight class.
Health = PC.default.Health; // Access the default value of Health in a variable class identified by PC.
Health = class<Pawn>(C).default.Health; // Access the default value of Health in a casted class expression.

Accessing static functions in a variable class: Static functions in a variable class may be called using the following syntax.

var class C;
var class<Pawn> PC;
class'SkaarjTrooper'.static.SomeFunction(); // Call a static function in a specific class.
PC.static.SomeFunction(); // Call a static function in a variable class.
class<Pawn>(C).static.SomeFunction(); // Call a static function in a casted class expression.

Advanced Technical Issues

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. on the 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 figure out 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.

Unreal COM integration: Unreal’s base UObject class derives from IUnknown in anticipation of making Unreal interoperable with the Component Object Model without requiring binary changes to objects. However, Unreal is not COM-aware at the moment and the benefits of integrating Unreal with COM are not yet clear, so this project is on indefinite hold.

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 two-pass: Unlike C++, UnrealScript is compiled in two distinct passes. In the first pass, variable, state and function definitions are parsed and remembered. 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.

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 at any time where all actors are at their lowest possible 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: 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.

UnrealScript programming strategy

Here I want to cover a few topics 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 at about 50 million base language instructions per second, while UnrealScript runs at about 2.5 million - a 20X performance hit. 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 fairy 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 occuring.
  • 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.

End


UScript Black Magic

The Dusty Tome of UnrealScript Black Magic

Brandon Reinhart
Epic Games, Inc.
brandon@epicgames.com
http://unreal.epicgames.com/

Last Updated: 12/08/99

I. Introduction

UnrealScript is a powerful programming language, but one steeped in the shrouded mystery of time. The purpose of this document is to reveal some of the more arcane methods of invoking this language. The tips and guides in this document will be compiled over time, from the author's one experience and from the experience of those who submit entries. For now, entries will be unsorted.

II. Index to Invocations

  • Using UWindow for Mutator Options
  • Using SpawnNotify to control object creation.
  • Using Entry for persistent object creation.
  • Package flags for package control.
  • Setting Package flags directly: ucc packageflag.
  • Using MS Developer Studio as an UnrealScript IDE.
  • Using BatchExport

III. UnrealScript Spellcraft

Using UWindow for Mutator Options

Author: Jack 'Mek' Porter
Email: jack@epicgames.com
Mutators with configuration options should make their own UWindow subclasses for configuration, and insert themselves into the (usually invisible) Mods menu, instead of using advanced options. Code examples of this can be found here: http://www.planetunreal.com/mutation/tutorials/mekoptions.html.

Using SpawnNotify to control object creation.

Author: Brandon 'GreenMarine' Reinhart
Email: brandon@epicgames.com
The SpawnNotify actor can be used to intercept and replace actors as they are instantiated. To do this subclass the SpawnNotify actor. In the default properties, set the ActorClass variable to the class of the actor you want to intercept. In the SpawnNotification method, destroy the actor that is passed in the parameter and spawn your new actor instead. This can be used to implement a custom PlayerReplicationInfo:

class MyPRISpawnNotify expands SpawnNotify;

event Actor SpawnNotification( Actor A )
{
    if (!A.IsA('MyPlayerReplicationInfo'))
    {
        A.Destroy;
        return Spawn(class'MyPlayerReplicationInfo');
    } else
        return A;
}

defaultproperties
{
    ActorClass=class'PlayerReplicatonInfo'
}

When you spawn your SpawnNotify object it will add itself to the level's SpawnNotify list. Objects in that list are queried with SpawnNotification whenever an object of their ActorClass is created. Notice that the example is careful to check the type of the actor. Failing to do this would cause an infinite loop (as our spawn notification destroyed the MyPlayerReplicationInfo it tried to create).

Using Entry for persistent object creation.

Author: Mongo
Email: mongo@planetunreal.com
Most UnrealScript objects are garbage collected when the level changes. If you have an object you need to be persistent across a level change, spawn it inside the 'Entry' level. The Unreal engine always has a small level loaded that exists for the lifetime of a game session, the 'Entry' level. Objects that are loaded inside of this level will not be garbage collected when the main level is destroyed. The UWindow system provides a method called GetEntryLevel() that I use inside the DecalStay mutator. Be very careful with this, however. If your persistent object references other objects in the main level, those objects will not be garbage collected. This can lead to very subtle and dangerous bugs.

Package flags for package control.

Author: Brandon 'GreenMarine' Reinhart
Email: brandon@epicgames.com
Every package has a set of three flags that can be controlled by a mod author. In order to set these flags, you need to be using 'ucc make' to compile your package. In the package's classes directory create a file called "MyPackage.upkg" where MyPackage is the name of the package. Put the following stuff in that file:

[Flags]
AllowDownload=False
ClientOptional=False
ServerSideOnly=False

When you rebuild your package, the settings you give each of these flags will be saved. AllowDownload will send your package to clients that connect to a server with that package set as a ServerPackage. ClientOptional means that the client doesn't have to have the package in order to connect to the server. A client optional package can be skipped during auto download and the client will still be allowed to connect. ServerSideOnly indicates whether or not the package should be loaded on the client side when a client joins a server running the package.

Client side mods that add new graphics or sounds will want to turn off ServerSideOnly and turn off ClientOptional. Server side mods will want to turn on ServerSideOnly and turn on ClientOptional.

Setting Package flags directly: ucc packageflag

Author: Jack 'Mek' Porter
Email: jack@epicgames.com
It is possible to set packageflags on compiled packages using the 'ucc packageflag' commandlet. Individual packageflags can be added or removed, type 'ucc help packageflag' for information.

Using MS Developer Studio as an UnrealScript IDE.

Author: Brandon 'GreenMarine' Reinhart
Email: brandon@epicgames.com
With just a little bit of work you can set up Microsoft Developer Studio as an UnrealScript development environment. The first thing to do is export the UnrealScript source files. Start UnrealED and switch the browser bar to "Classes." Hit the Export All button to export the source files to disk.

Now start DevStu and create a new workspace in your Unreal Tournament directory. Add an empty windows DLL project for each source package (Core, Engine, Botpack, etc) and create a folder until the FileView called Classes. Set the folder's extension type to uc. Finally add each package's source files to their respective folder.

You can compile inside DevStu by using a custom batch file. Create a MakePackage.bat inside your UnrealTournament system directory and add some commands like this:

@ECHO OFF
del %1
ucc make

This will rebuild the argument (for example, MakePackage Wookie.u would delete Wookie.u and rebuild it). Now add a custom tool to the tools list in DevStudio. If you select "Use Output Window" the results of the build will be dumped to the Build window in Dev Studio. Finally, you can attach your custom tool to a custom button and put it on your button bar for fast access.

If you want limited context highlighting, you can run regedit and find the following key:

[HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Text Editor\Tabs/Language Settings\C/C++]

Add "uc" to the FileExtensions list. Make sure DevStudio is shutdown first, otherwise it'll write over your changes when you exit. Now restart DevStudio and you've got some limited context highlighting for UnrealScript!

Using BatchExport

Author: Nathaniel "^Soul^" Brown
Email: Soul@PlanetUnreal.com

UCC has many commandlets which are interfaces from UCC to Unreal. They are defined in Object->Commandlet. They can be scripted(such as Object->Commandlet->HelloWorldCommandlet) or done in C++, the later of which gives you better access to unreal and its package format. One of the more useful commandlets is BatchExport. BatchExport allows you to export all files of a specific type from a .u, .uax, .umx, and .utx. From the command prompt you just type 'ucc.exe batchexport 'Item type' 'File Extension 'path''. The valid types are 'Class', 'Sound', 'Texture', and 'Music'. So say for instance you wanted to export all the class files out of botpack.u without opening UnrealED, you would go to the command prompt and type 'ucc batchexport botpack.u class uc x:\tournament\botpack'.


ActorClass Reference

The Actor Class

Written by Valiant - 11 June 1998

Copyright

Unreal™ © 1998 Epic Megagames, Inc. All Rights Reserved.

Version Information

Based upon Unreal Version 1.0. Please send comments to Valiant.

Class Definition

class Actor expands Object
 abstract
intrinsic;
The base class of all actors.

Imported Data

#exec Texture Import File=Textures\S_Actor.pcx Name=S_Actor Mips=Off Flags=2
Default texture loading.

Class Enums and Related Class Data Members

EDodgeDir
var enum EDodgeDir
{
DODGE_None,
DODGE_Left,
DODGE_Right,
DODGE_Forward,
DODGE_Back,
DODGE_Active,
DODGE_Done
} DodgeDir;
Enumerators for dodging move direction.

EDrawType
var(Display) enum EDrawType
{
DT_None,
DT_Sprite,
DT_Mesh,
DT_Brush,
DT_RopeSprite,
DT_VerticalSprite,
DT_Terraform,
DT_SpriteAnimOnce,
} DrawType;
Enumerator for types of drawing effects.

EInputAction
enum EInputAction
{
IST_None, // Not performing special input processing.
IST_Press, // Handling a keypress or button press.
IST_Hold, // Handling holding a key or button.
IST_Release, // Handling a key or button release.
IST_Axis, // Handling analog axis movement.
};
Enumerator for input system states.

EInputKey
enum EInputKey
{
/*00*/ IK_None, IK_LeftMouse, IK_RightMouse, IK_Cancel,
/*04*/ IK_MiddleMouse, IK_Unknown05, IK_Unknown06, IK_Unknown07,
/*08*/ IK_Backspace, IK_Tab, IK_Unknown0A, IK_Unknown0B,
/*0C*/ IK_Unknown0C, IK_Enter, IK_Unknown0E, IK_Unknown0F,
/*10*/ IK_Shift, IK_Ctrl, IK_Alt, IK_Pause,
/*14*/ IK_CapsLock, IK_Unknown15, IK_Unknown16, IK_Unknown17,
/*18*/ IK_Unknown18, IK_Unknown19, IK_Unknown1A, IK_Escape,
/*1C*/ IK_Unknown1C, IK_Unknown1D, IK_Unknown1E, IK_Unknown1F,
/*20*/ IK_Space, IK_PageUp, IK_PageDown, IK_End,
/*24*/ IK_Home, IK_Left, IK_Up, IK_Right,
/*28*/ IK_Down, IK_Select, IK_Print, IK_Execute,
/*2C*/ IK_PrintScrn, IK_Insert, IK_Delete, IK_Help,
/*30*/ IK_0, IK_1, IK_2, IK_3,
/*34*/ IK_4, IK_5, IK_6, IK_7,
/*38*/ IK_8, IK_9, IK_Unknown3A, IK_Unknown3B,
/*3C*/ IK_Unknown3C, IK_Unknown3D, IK_Unknown3E, IK_Unknown3F,
/*40*/ IK_Unknown40, IK_A, IK_B, IK_C,
/*44*/ IK_D, IK_E, IK_F, IK_G,
/*48*/ IK_H, IK_I, IK_J, IK_K,
/*4C*/ IK_L, IK_M, IK_N, IK_O,
/*50*/ IK_P, IK_Q, IK_R, IK_S,
/*54*/ IK_T, IK_U, IK_V, IK_W,
/*58*/ IK_X, IK_Y, IK_Z, IK_Unknown5B,
/*5C*/ IK_Unknown5C, IK_Unknown5D, IK_Unknown5E, IK_Unknown5F,
/*60*/ IK_NumPad0, IK_NumPad1, IK_NumPad2, IK_NumPad3,
/*64*/ IK_NumPad4, IK_NumPad5, IK_NumPad6, IK_NumPad7,
/*68*/ IK_NumPad8, IK_NumPad9, IK_GreyStar, IK_GreyPlus,
/*6C*/ IK_Separator, IK_GreyMinus, IK_NumPadPeriod, IK_GreySlash,
/*70*/ IK_F1, IK_F2, IK_F3, IK_F4,
/*74*/ IK_F5, IK_F6, IK_F7, IK_F8,
/*78*/ IK_F9, IK_F10, IK_F11, IK_F12,
/*7C*/ IK_F13 , IK_F14, IK_F15, IK_F16,
/*80*/ IK_F17, IK_F18, IK_F19, IK_F20,
/*84*/ IK_F21 , IK_F22, IK_F23, IK_F24,
/*88*/ IK_Unknown88, IK_Unknown89, IK_Unknown8A, IK_Unknown8B,
/*8C*/ IK_Unknown8C, IK_Unknown8D, IK_Unknown8E, IK_Unknown8F,
/*90*/ IK_NumLock, IK_ScrollLock , IK_Unknown92, IK_Unknown93,
/*94*/ IK_Unknown94, IK_Unknown95, IK_Unknown96, IK_Unknown97,
/*98*/ IK_Unknown98, IK_Unknown99, IK_Unknown9A, IK_Unknown9B,
/*9C*/ IK_Unknown9C, IK_Unknown9D, IK_Unknown9E, IK_Unknown9F,
/*A0*/ IK_LShift, IK_RShift, IK_LControl, IK_RControl,
/*A4*/ IK_UnknownA4, IK_UnknownA5, IK_UnknownA6, IK_UnknownA7,
/*A8*/ IK_UnknownA8, IK_UnknownA9, IK_UnknownAA, IK_UnknownAB,
/*AC*/ IK_UnknownAC, IK_UnknownAD, IK_UnknownAE, IK_UnknownAF,
/*B0*/ IK_UnknownB0, IK_UnknownB1, IK_UnknownB2, IK_UnknownB3,
/*B4*/ IK_UnknownB4, IK_UnknownB5, IK_UnknownB6, IK_UnknownB7,
/*B8*/ IK_UnknownB8, IK_UnknownB9, IK_Semicolon, IK_Equals,
/*BC*/ IK_Comma, IK_Minus, IK_Period, IK_Slash,
/*C0*/ IK_Tilde, IK_UnknownC1, IK_UnknownC2, IK_UnknownC3,
/*C4*/ IK_UnknownC4, IK_UnknownC5, IK_UnknownC6, IK_UnknownC7,
/*C8*/ IK_Joy1, IK_Joy2, IK_Joy3, IK_Joy4,
/*CC*/ IK_Joy5, IK_Joy6, IK_Joy7, IK_Joy8,
/*D0*/ IK_Joy9, IK_Joy10, IK_Joy11, IK_Joy12,
/*D4*/ IK_Joy13, IK_Joy14, IK_Joy15, IK_Joy16,
/*D8*/ IK_UnknownD8, IK_UnknownD9, IK_UnknownDA, IK_LeftBracket,
/*DC*/ IK_Backslash, IK_RightBracket, IK_SingleQuote, IK_UnknownDF,
/*E0*/ IK_JoyX, IK_JoyY, IK_JoyZ, IK_JoyR,
/*E4*/ IK_MouseX, IK_MouseY, IK_MouseZ, IK_MouseW,
/*E8*/ IK_JoyU, IK_JoyV, IK_UnknownEA, IK_UnknownEB,
/*EC*/ IK_MouseWheelUp, IK_MouseWheelDown, IK_Unknown10E, UK_Unknown10F,
/*F0*/ IK_JoyPovUp, IK_JoyPovDown, IK_JoyPovLeft, IK_JoyPovRight,
/*F4*/ IK_UnknownF4, IK_UnknownF5, IK_Attn, IK_CrSel,
/*F8*/ IK_ExSel, IK_ErEof, IK_Play, IK_Zoom,
/*FC*/ IK_NoName, IK_PA1, IK_OEMClear
};
Enumerator for input keys.

ELightEffect
var(Lighting) enum ELightEffect
{
LE_None,
LE_TorchWaver,
LE_FireWaver,
LE_WateryShimmer,
LE_Searchlight,
LE_SlowWave,
LE_FastWave,
LE_CloudCast,
LE_StaticSpot,
LE_Shock,
LE_Disco,
LE_Warp,
LE_Spotlight,
LE_NonIncidence,
LE_Shell,
LE_OmniBumpMap,
LE_Interference,
LE_Cylinder,
LE_Rotor,
LE_Unused
} LightEffect;
Enumerator for different spatial light effects to use.

ELightType
var(Lighting) enum ELightType
{
LT_None,
LT_Steady,
LT_Pulse,
LT_Blink,
LT_Flicker,
LT_Strobe,
LT_BackdropLight,
LT_SubtlePulse,
LT_TexturePaletteOnce,
LT_TexturePaletteLoop
} LightType;
Enumerator for different light modulation types.

EMusicTransition
enum EMusicTransition
{
MTRAN_None,
MTRAN_Instant,
MTRAN_Segue,
MTRAN_Fade,
MTRAN_FastFade,
MTRAN_SlowFade,
};
Enumerators for music transitions.

ENetRole
enum ENetRole
{
ROLE_None,
ROLE_DumbProxy,
ROLE_SimulatedProxy,
ROLE_AutonomousProxy,
ROLE_Authority,
};
Enumerator for the deferent roles this actor may play in a net based game.

EPhysics
var(Movement) const enum EPhysics
{
PHYS_None,
PHYS_Walking,
PHYS_Falling,
PHYS_Swimming,
PHYS_Flying,
PHYS_Rotating,
PHYS_Projectile,
PHYS_Rolling,
PHYS_Interpolating,
PHYS_MovingBrush,
PHYS_Spider,
PHYS_Trailer
} Physics;
Enumerators for supported physics modes.

ERenderStyle
var(Display) enum ERenderStyle
{
STY_None,
STY_Normal,
STY_Masked,
STY_Translucent,
STY_Modulated,
} Style;
Enumerators for different styles of rendering sprites and meshes.

ESoundSlot
enum ESoundSlot
{
SLOT_None,
SLOT_Misc,
SLOT_Pain,
SLOT_Interact,
SLOT_Ambient,
SLOT_Talk,
SLOT_Interface,
};
Enumerators for different sound slots for actors.

ETravelType
enum ETravelType
{
TRAVEL_Absolute, // Absolute URL.
TRAVEL_Partial, // Partial (carry name, reset server).
TRAVEL_Relative, // Relative URL.
};
Enumerator for travelling from server to server.

Class Data Members

Acceleration
var vector Acceleration;
Actor’s current acceleration.

bAlwaysRelevant
var(Advanced) bool bAlwaysRelevant;
Never destroy based on game.

bAlwaysTick
var Const bool bAlwaysTick;
Update even when players-only.

AmbientGlow
var(Display) byte AmbientGlow;
Ambient brightness, or 255=pulsing.

AmbientSound
var(Sound) sound AmbientSound;
Ambient sound effect.

AnimFrame
var(Display) float AnimFrame;
Current animation frame, 0.0 to 1.0.

AnimLast
var float AnimLast;
Last frame of current animation.

AnimMinRate
var float AnimMinRate;
Minimum rate for velocity-scaled animation.

AnimRate
var(Display) float AnimRate;
Animation rate in frames per second, 0=none, negative=velocity scaled.

AnimSequence
var(Display) name AnimSequence;
Current animation sequence being played.

bActorShadows
var(Lighting) bool bActorShadows;
This light casts actor shadows.

bAnimFinished
var bool bAnimFinished;
Unlooped animation sequence has finished.

bAnimLoop
var bool bAnimLoop;
Whether animation is looping.

bAnimNotify
var bool bAnimNotify;
Whether a notify is applied to the current sequence of animation.

bAssimilated
var transient const bool bAssimilated;
Actor dynamics are assimilated in world geometry.

bBlockActors
var(Collision) bool bBlockActors;
Blocks other nonplayer actors.

bBlockPlayers
var(Collision) bool bBlockPlayers;
Blocks other player actors.

bBounce
var(Movement) bool bBounce;
Actor bounces when it hits ground fast.

bCanTeleport
var(Advanced) bool bCanTeleport;
This actor can be teleported.

bCollideActors
var(Collision) const bool bCollideActors;
Collides with other actors.

bCollideWhenPlacing
var(Advanced) bool bCollideWhenPlacing;
This actor collides with the world when placing.

bCollideWorld
var(Collision) bool bCollideWorld;
Collides with the world.

bCorona
var(Lighting) bool bCorona;
This light uses Skin as a corona.

bDeleteMe
var const bool bDeleteMe;
Actor is about to be deleted.

bDirectional
var(Advanced) bool bDirectional;
Actor shows direction arrow during editing.

bDynamicLight
var bool bDynamicLight;
Temporarily treat this as a dynamic light.

bEdLocked
var bool bEdLocked;
Actor is locked in editor, therefore, no movement or rotation.

bEdShouldSnap
var(Advanced) bool bEdShouldSnap;
Actor should be snapped to grid in UnrealEd.

bEdSnap
var transient bool bEdSnap;
Actor is snapped to grid in UnrealEd.

bFixedRotationDir
var(Movement) bool bFixedRotationDir;
Actor has a fixed direction of rotation.

bForceStasis
var(Advanced) bool bForceStasis;
Actor is forced into stasis when not recently rendered, even if physics not PHYS_None or PHYS_Rotating.

bHidden
var(Advanced) bool bHidden;
Actor is hidden during gameplay.

bHiddenEd
var(Advanced) bool bHiddenEd;
Actor is hidden during editing.

bHighDetail
var(Advanced) bool bHighDetail;
Actor only shows up on high detail mode.

bHighlighted
var const bool bHighlighted;
Actor is highlighted in UnrealEd.

bHurtEntry
var bool bHurtEntry;
keep HurtRadius from being reentrant

bInterpolating
var bool bInterpolating;
Actor is performing an interpolating operation.

bIsItemGoal
var(Advanced) bool bIsItemGoal;
This actor counts in the "item" count.

bIsKillGoal
var(Advanced) bool bIsKillGoal;
This actor counts in the "death" toll.

bIsMover
var Const bool bIsMover;
Is a mover.

bIsPawn
var const bool bIsPawn;
Actor is a pawn.

bIsSecretGoal
var(Advanced) bool bIsSecretGoal;
This actor counts in the "secret" total.

bJustTeleported
var const bool bJustTeleported;
Used by engine physics - not valid for scripts.

bLensFlare
var(Lighting) bool bLensFlare;
Whether or not to use zone lens flare.

bLightChanged
var transient bool bLightChanged;
Recalculate this light’s lighting now.

bMemorized
var const bool bMemorized;
Actor is remembered in UnrealEd.

bMeshCurvy
var(Display) bool bMeshCurvy;
Curvy mesh.

bMeshEnviroMap
var(Display) bool bMeshEnviroMap;
Environment-map the mesh.

bMovable
var(Advanced) bool bMovable;
Actor is capable of travelling among servers.

bNetFeel
var const bool bNetFeel;
Player collides with/feels this actor in network play. . Symmetric network flag, valid during replication only.

bNetHear
var const bool bNetHear;
Player hears this actor in network play. Symmetric network flag, valid during replication only.

bNetInitial
var const bool bNetInitial;
Initial network update flag. Symmetric network flag, valid during replication only.

bNetOptional
var const bool bNetOptional;
Actor should only be replicated if bandwidth available. Symmetric network flag, valid during replication only.

bNetOwner
var const bool bNetOwner;
Player owns this actor. Symmetric network flag, valid during replication only.

bNetSee
var const bool bNetSee;
Player sees it in network play. Symmetric network flag, valid during replication only.

bNoDelete
var(Advanced) const bool bNoDelete;
Actor cannot be deleted during play.

bNoSmooth
var(Display) bool bNoSmooth;
Don't smooth actor's texture.

bOnlyOwnerSee
var(Advanced) bool bOnlyOwnerSee;
Only owner can see this actor.

bParticles
var(Display) bool bParticles;
Mesh is a particle system.

bProjTarget
var(Collision) bool bProjTarget;
Projectiles should potentially target this actor.

bRotateToDesired
var(Movement) bool bRotateToDesired;
Actor rotates to DesiredRotation.

Brush
var const export model Brush;
Brush if DrawType=DT_Brush.

bSelected
var const bool bSelected;
Actor is selected in UnrealEd.

bShadowCast
var(Display) bool bShadowCast;
Casts shadows. Not yet implemented.

bSimulatedPawn
var const bool bSimulatedPawn;
True if this actor is a Pawn and a simulated proxy. Symmetric network flag, valid during replication only.

bSpecialLit
var(Lighting) bool bSpecialLit;
Only affects special-lit surfaces.

bStasis
var(Advanced) bool bStasis;
In standalone games, actor is turned off if not in a recently rendered zone, or the actor is turned off if bCanStasis == True and physics mode is PHYS_None or PHYS_Rotating.

bStatic
var(Advanced) const bool bStatic;
Actor does not move or change over time.

bTempEditor
var transient const bool bTempEditor;
Internal UnrealEd use.

bTicked
var transient const bool bTicked;
Actor has been updated.

bTimerLoop
var bool bTimerLoop;
If True then the actor Timer loops, else it is one-shot.

bTravel
var(Advanced) bool bTravel;
Actor is capable of travelling among servers.

bUnlit
var(Display) bool bUnlit;
Lights don't affect actor.

Buoyancy
var(Movement) float Buoyancy;
Actor’s water buoyancy value.

CollisionHeight
var(Collision) const float CollisionHeight;
Half-height cylinder.

CollisionRadius
var(Collision) const float CollisionRadius;
Radius of collision cyllinder.

ColLocation
var const vector ColLocation;
Actor's old location one move ago.

DesiredRotation
var(Movement) rotator DesiredRotation;
Physics subsystem will rotate pawn to this if bRotateToDesired.

DrawScale
var(Display) float DrawScale;
Scaling factor, 1.0=normal size.

Fatness
var(Display) byte Fatness;
Fatness (mesh distortion).

Group
var(Object) name Group;
???

InitialState
var(Object) name InitialState;
???

LifeSpan
var(Advanced) float LifeSpan;
How long the actor lives before dying, 0=forever. Used for executing timer-related events for the actor.

LightBrightness
var(LightColor) byte LightBrightness;
Light brightness value.

LightCone
var(LightColor) byte LightCone;
Light cone value.

LightHue
var(LightColor) byte LightHue;
Light hue value.

LightPeriod
var(Lighting) byte LightPeriod;
Light period value.

LightPhase
var(Lighting) byte LightPhase;
Light phase value.

LightRadius
var(Lighting) byte LightRadius;
Light radius value.

LightSaturation
var(LightColor) byte LightSaturation;
Light saturation value.

Location
var(Movement) const vector Location;
Actor's location; use Move to set.

Mass
var(Movement) float Mass;
Mass of this actor.

Mesh
var(Display) mesh Mesh;
Mesh if DrawType=DT_Mesh.

NetPriority
var(Networking) float NetPriority;
Higher priorities means update it more frequently.

OldAnimRate
var float OldAnimRate;
Animation rate of previous animation (= AnimRate until animation completes).

OldLocation
var const vector OldLocation;
Actor's old location one tick ago.

Owner
var const Actor Owner;
Owner of this actor.

PhysAlpha
var float PhysAlpha;
Interpolating position, 0.0-1.0.

PhysRate
var float PhysRate;
Interpolation rate per second.

PrePivot
var vector PrePivot;
Offset from box center for drawing.

RemoteRole
var(Networking) ENetRole RemoteRole;
Actor’s role in the current networked game on all other machines other than it’s original machine.

Role
var ENetRole Role;
Actor/s role in the current networked game on it’s original machine.

Rotation
var(Movement) const rotator Rotation;
Actor’s current rotation.

RotationRate
var(Movement) rotator RotationRate;
Change in rotation per second.

ScaleGlow
var(Display) float ScaleGlow;
Multiplies lighting.

SimAnim
var plane SimAnim;
Replicated to simulated proxies.

Skin
var(Display) texture Skin;
Special skin or environment map texture.

SoundPitch
var(Sound) byte SoundPitch;
Ambient sound pitch shift, 64.0=none.

SoundRadius
var(Sound) byte SoundRadius;
Radius of ambient sound.

SoundVolume
var(Sound) byte SoundVolume;
Volume of ambient sound.

Sprite
var(Display) texture Sprite;
Sprite texture if DrawType=DT_Sprite.

Texture
var(Display) texture Texture;
Misc. texture.

TimerCounter
var const float TimerCounter;
Counts up until it reaches TimerRate. Used for executing timer-related events for the actor.

TimerRate
var float TimerRate;
Timer event, 0=no timer. Used for executing timer-related events for the actor.

TransientSoundVolume
var(Sound) float TransientSoundVolume;
Volume of regular (non-ambient) sounds.

TweenRate
var float TweenRate;
Animation tween-into rate.

Velocity
var(Movement) vector Velocity;
Actor’s player’s pulse rate . . . just wanted to see if you were awake. ;-) Actor’s current velocity.

VolumeBrightness
var(Lighting) byte VolumeBrightness;
Light volume brightness value.

VolumeFog
var(Lighting) byte VolumeFog;
Light volume fog value.

VolumeRadius
var(Lighting) byte VolumeRadius;
Light volume radius value.

Scriptable Class Data Members

Base
var const Actor Base;
Moving brush actor we're standing on.

Event
var(Events) name Event;
The event this actor causes.

Instigator
var Pawn Instigator;
Pawn responsible for damage.

Inventory
var Inventory Inventory;
Inventory chain.

LevelInfo
var const LevelInfo Level;
Level this actor is on.

Region
var const PointRegion Region;
Region this actor is in.

Tag
var(Events) name Tag;
Actor's tag name.

Target
var Actor Target;
Actor we're aiming at (other uses as well).

XLevel
var const Level XLevel;
Level object.

Gameplay Scenarios Class Data Members

bDifficulty0
var(Filter) bool bDifficulty0;
Actor appears in difficulty level 0 gameplay scenarios.

bDifficulty1
var(Filter) bool bDifficulty1;
Actor appears in difficulty level 1 gameplay scenarios.

bDifficulty2
var(Filter) bool bDifficulty2;
Actor appears in difficulty level 2 gameplay scenarios.

bDifficulty3
var(Filter) bool bDifficulty3;
Actor appears in difficulty level 3 gameplay scenarios.

bSinglePlayer
var(Filter) bool bSinglePlayer;
Actor appears in single player gameplay scenarios.

bNet
var(Filter) bool bNet;
Actor appears in regular network play gameplay scenarios.

bNetSpecial
var(Filter) bool bNetSpecial;
Actor appears in special network gameplay mode.

OddsOfAppearing
var(Filter) float OddsOfAppearing;
Chance the actor will appear in relevant gameplay modes, values of 0.0 to 1.0, 1.0=always appears.

Internal Use Class Data Members

CollisionTag, ExtraTag, LightingTag, NetTag, OtherTag, SpecialTag
var const transient int CollisionTag;
var const transient int ExtraTag;
var const transient int LightingTag;
var const transient int NetTag;
var const transient int OtherTag;
var const transient int SpecialTag;
All tag variables for internal use. Purposes unknown.

Deleted
var const actor Deleted;
Next actor in just-deleted chain.

LatentActor
var const actor LatentActor;
Internal latent function use.

LatentByte
var const byte LatentByte;
Internal latent function use.

LatentFloat
var const float LatentFloat;
Internal latent function use.

LatentInt
var const int LatentInt;
Internal latent function use.

MiscNumber
var const byte MiscNumber;
Internal use.

StandingCount
var const byte StandingCount;
Count of actors standing on this actor.

Touching[4]
var const actor Touching[4];
List of touching actors.

Class Structure Definitions

PointRegion
struct PointRegion
{
var zoneinfo Zone; // Zone.
var int iLeaf; // Bsp leaf.
var byte ZoneNumber; // Zone number, to be eliminated!!
};
Identifies a unique convex volume in the world.

Class Information for Network Replication

???. I have my guesses about this special piece of code for dealing with networking. I still think it best we wait for Tim Sweeney to enlighten us. If you have some thoughts on this piece of code please email me at Valiant.

replication
{
// Relationships.
reliable if( Role==ROLE_Authority )
Owner, Role, RemoteRole;
reliable if( Role==ROLE_Authority && bNetOwner )
bNetOwner, Inventory;
// Ambient sound.
reliable if( Role==ROLE_Authority )
AmbientSound;
reliable if( Role==ROLE_Authority && AmbientSound!=None )
SoundRadius, SoundVolume, SoundPitch;
// Collision.
reliable if( Role==ROLE_Authority )
bCollideActors;
reliable if( Role==ROLE_Authority )
bCollideWorld;
reliable if( Role==ROLE_Authority && bCollideActors )
bBlockActors, bBlockPlayers;
reliable if( Role==ROLE_Authority && (bCollideActors || bCollideWorld) )
CollisionRadius, CollisionHeight;
// Location.
unreliable if( Role==ROLE_Authority && (bNetInitial || bSimulatedPawn ||
RemoteRole<ROLE_SimulatedProxy) )
Location, Rotation;
unreliable if( Role==ROLE_Authority && (bNetInitial || bSimulatedPawn) &&
RemoteRole==ROLE_SimulatedProxy )
Base;
// Velocity.
unreliable if( (RemoteRole==ROLE_SimulatedProxy && (bNetInitial || bSimulatedPawn)) || bIsMover )
Velocity;
// Physics.
unreliable if( RemoteRole==ROLE_SimulatedProxy && bNetInitial )
Physics, Acceleration, bBounce;
unreliable if( RemoteRole==ROLE_SimulatedProxy && Physics==PHYS_Rotating && bNetInitial )
bFixedRotationDir, bRotateToDesired, RotationRate, DesiredRotation;
// Animation.
unreliable if( DrawType==DT_Mesh && (RemoteRole<=ROLE_SimulatedProxy) )
AnimSequence;
unreliable if( DrawType==DT_Mesh && (RemoteRole==ROLE_SimulatedProxy) )
bAnimNotify;
unreliable if( DrawType==DT_Mesh && (RemoteRole<ROLE_AutonomousProxy) )
SimAnim, AnimMinRate;
// Rendering.
reliable if( Role==ROLE_Authority )
bHidden, bOnlyOwnerSee;
unreliable if( Role==ROLE_Authority ) // and see...
Texture, DrawScale, PrePivot, DrawType, AmbientGlow, Fatness, ScaleGlow, bUnlit, bNoSmooth,
bShadowCast, bActorShadows;
unreliable if( Role==ROLE_Authority && DrawType==DT_Sprite && !bHidden && (!bOnlyOwnerSee || bNetOwner) )
Sprite;
unreliable if( Role==ROLE_Authority && DrawType==DT_Mesh )
Mesh, bMeshEnviroMap, bMeshCurvy, Skin;
unreliable if( Role==ROLE_Authority && DrawType==DT_Brush )
Brush;
// Lighting.
unreliable if( Role==ROLE_Authority )
LightType;
unreliable if( Role==ROLE_Authority && LightType!=LT_None )
LightEffect, LightBrightness, LightHue, LightSaturation,
LightRadius, LightPeriod, LightPhase, LightCone,
VolumeBrightness, VolumeRadius,
bSpecialLit;
// Messages
unreliable if( Role < ROLE_Authority )
BroadcastMessage;
}

Class Intrinsic Operators and Functions

Vector Operators

-
intrinsic(211) static final preoperator vector - ( vector A );
Negation operator

*
intrinsic(212) static final operator(16) vector * ( vector A, float B );
intrinsic(213) static final operator(16) vector * ( float A, vector B );
intrinsic(296) static final operator(16) vector * ( vector A, vector B );
Multiplication operator

/
intrinsic(214) static final operator(16) vector / ( vector A, float B );
Division operator

+
intrinsic(215) static final operator(20) vector + ( vector A, vector B );
Addition operator

-
intrinsic(216) static final operator(20) vector - ( vector A, vector B );
Subtraction operator

<<
intrinsic(275) static final operator(22) vector << ( vector A, rotator B );
Left rotation operator

>>
intrinsic(276) static final operator(22) vector >> ( vector A, rotator B );
Right rotation operator

==
intrinsic(217) static final operator(24) bool == ( vector A, vector B );
Equality operator

!=
intrinsic(218) static final operator(26) bool != ( vector A, vector B );
Inequality operator

Dot
intrinsic(219) static final operator(16) float Dot ( vector A, vector B );
Dot product operator

Cross
intrinsic(220) static final operator(16) vector Cross ( vector A, vector B );
Cross product operator

*=
intrinsic(221) static final operator(34) vector *= ( out vector A, float B );
intrinsic(297) static final operator(34) vector *= ( out vector A, vector B );
Multiplication assignment operator

/=
intrinsic(222) static final operator(34) vector /= ( out vector A, float B );
Division assignment operator

+=
intrinsic(223) static final operator(34) vector += ( out vector A, vector B );
Addition assignment operator

-=
intrinsic(224) static final operator(34) vector -= ( out vector A, vector B );
Subtraction assignment operator

Vector Functions

VSize
intrinsic(225) static final function float VSize ( vector A );
Get the vertical size of a vector.

Normal
intrinsic(226) static final function vector Normal ( vector A );
Get the normal of a vector.

Invert
intrinsic(227) static final function Invert ( out vector X, out vector Y, out vector Z );
Invert a vector.

Vrand
intrinsic(252) static final function vector VRand ( );
???. Please email Valiant if you know what this does.

MirrorVectorByNormal
intrinsic(300) static final function vector MirrorVectorByNormal( vector Vect, vector Normal );
Return the mirror about the given normal of a vector.

Rotator Operators

==
intrinsic(142) static final operator(24) bool == ( rotator A, rotator B );
Equality operator

!=
intrinsic(203) static final operator(26) bool != ( rotator A, rotator B );
Inequality operator

*
intrinsic(287) static final operator(16) rotator * ( rotator A, float B );
intrinsic(288) static final operator(16) rotator * ( float A, rotator B );
Multiplication operator

/
intrinsic(289) static final operator(16) rotator / ( rotator A, float B );
Division operator

*=
intrinsic(290) static final operator(34) rotator *= ( out rotator A, float B );
Multiplication assignment operator

/=
intrinsic(291) static final operator(34) rotator /= ( out rotator A, float B );
Division assignment operator

+
intrinsic(316) static final operator(20) rotator + ( rotator A, rotator B );
Addition operator

-
intrinsic(317) static final operator(20) rotator - ( rotator A, rotator B );
Subtraction operator

+=
intrinsic(318) static final operator(34) rotator += ( out rotator A, rotator B );
Addition assignment operator

-=
intrinsic(319) static final operator(34) rotator -= ( out rotator A, rotator B );
Subtraction assignment operator

Rotator Functions

GetAxes
intrinsic(229) static final function GetAxes ( rotator A, out vector X, out vector Y, out vector Z );
Get X,Y and Z axes values for the given rotator.

GetUnAxes
intrinsic(230) static final function GetUnAxes ( rotator A, out vector X, out vector Y, out vector Z );
???. Email me at Valiant if you know what this does.

RotRand
intrinsic(320) static final function rotator RotRand ( optional bool bRoll );
Return randomized rotator.

Iterator Functions

AllActors
intrinsic(304) final iterator function AllActors(class<actor> BaseClass, out actor Actor, optional name MatchTag);
Iterates through all actors in the level with the specified tag. If you don’t specify a tag, it iterates through all actors in the level. This is pretty slow if you specify a tag, and very slow if you don’t.

BasedActors
intrinsic(306) final iterator function BasedActors(class<actor> BaseClass, out actor Actor);
Iterates through all actors directly based on this actor.

ChildActors
intrinsic(305) final iterator function ChildActors(class<actor> BaseClass, out actor Actor);
Iterates through all actors owned by this actor.

RadiusActors
intrinsic(310) final iterator function RadiusActors(class<actor> BaseClass, out actor Actor, float Radius, optional vector Loc);
Iterates through all actors within a specified radius. This is fast.

TouchingActors
intrinsic(307) final iterator function TouchingActors(class<actor> BaseClass, out actor Actor);
Iterates through all actors touching this actor. This is fast.

TraceActors
intrinsic(309) final iterator function TraceActors(class<actor> BaseClass, out actor Actor, out vector HitLoc, out vector HitNorm, vector End, optional vector Start, optional vector Extent);
Iterates through all actors hit by a trace along a line with an optional collision extent. This is pretty fast.

VisibleActors
intrinsic(311) final iterator function VisibleActors(class<actor> BaseClass, out actor Actor, optional float Radius, optional vector Loc);
Iterates through all actors visible to this actor (using a simple line trace, rather than exact mesh-to-mesh visible). This is very, very slow.

VisibleCollidingActors
intrinsic(312) final iterator function VisibleCollidingActors(class<actor> BaseClass, out actor Actor, optional float Radius, optional vector Loc);
Iterates through all actors visible to this actor which have collision enabled; this is much faster than VisibleActors.

Class Member Functions

AnimEnd
event AnimEnd();
Called when the currently playing or looping animation reaches the end of the sequence.

Attach
event Attach( Actor Other );
Some other actor has set its base to this actor.

BaseChange
event BaseChange();
This actor’s base has changed.

BeginEvent
event BeginEvent();
A simple protocal to indicate that this actor called a Trigger() function that began a complex chain of events. This enables switches to be coordinated with doors intelligently. For example, you press a switch, the door switch moves in, it triggers a door which calls your BeginEvent() then slowly opens, then the door calls your EndEvent(). Nowyou move the button back out to give the user feedback that the button is finished with what it was doing.

BeginPlay
event BeginPlay();
Called when a level is entered for playing.

BroadcastMessage
function BroadcastMessage( coerce string[240] Msg, bool bBeep )
{
local Pawn P;
if ( Level.Game.AllowsBroadcast(self, Len(Msg)) )
for( P=Level.PawnList; P!=None; P=P.nextPawn )
if( P.bIsPlayer )
P.ClientMessage( Msg );
}
Broadcast a message to all players.

Bump
event Bump( Actor Other );
This actor has bumped into an impenetrable actor.

Destroy
intrinsic(279) final function bool Destroy();
Destroy this actor. Returns true if destroyed, false if indestructable. Destruction is latent. It occurs at the end of the tick.

Destroyed
event Destroyed();
Called immediately when an actor is destroyed.

Detach
event Detach( Actor Other );
Some other actor based on this one is no longer based on this actor.

EncroachedBy
event EncroachedBy( actor Other );
This actor has been successfully encroached by (overlapped by) another actor. This is where players may opt to telefrag themselves or ignore the encroach.

EncroachingOn
event bool EncroachingOn( actor Other );
This actor is now encroaching on (overlapping with) another actor as a result of moving and it may return true to abort the move, or false to continue the move.

EndEvent
event EndEvent();
The event called when BeginEvent() has ended.

EndedRotation
event EndedRotation();
Called when the actor’s current rotation has ended.

Error
intrinsic(233) final function Error( coerce string[255] S );
Handle an error and kill this one actor. Causes a critical error; exits the engine.

Expired
event Expired();
Called immediately before Destroyed() when an actor’s LifeTime has counted downward past zero.

Falling
event Falling();
This actor has transitioned into PHYS_Falling.

FinishAnim
intrinsic(261) final latent function FinishAnim();
Waits for the currently playing or looping animation to reach the end of the sequence.

FinishInterpolation
intrinsic(301) final latent function FinishInterpolation();
Only relevent with the physics mode PHYS_Interpolating. Waits until the next interpolation point is reached.

GainedChild
event GainedChild( Actor Other );
Another actor has done a SetOwner() to this actor.

GetAnimGroup
intrinsic(293) final function name GetAnimGroup( name Sequence );
Returns the group name of the specified animation sequence.

GetMapName
intrinsic(539) final function string[32] GetMapName( string[32] NameEnding, string[32] MapName, int Dir );
Returns the name of the map specified.

GetNextSkin

intrinsic(545) final function string[64] GetNextSkin( string[64] Prefix, string[64] CurrentSkin, int Dir );

Returns the name of the next skin in the current file of skins being accessed.

HitWall

event HitWall( vector HitNormal, actor HitWall );

This actor ran into a wall while moving.

HurtRadius
final function HurtRadius( float DamageAmount, float DamageRadius, name DamageName, float Momentum, vector HitLocation )
{
local actor Victims;
local float damageScale, dist;
local vector dir;
if( bHurtEntry )
return;
bHurtEntry = true;
foreach VisibleCollidingActors( class 'Actor', Victims, DamageRadius, HitLocation )
{
if( Victims != self )
{
dir = Victims.Location - HitLocation;
dist = FMax(1,VSize(dir));
dir = dir/dist;
damageScale = 1 - FMax(0,(dist - Victims.CollisionRadius)/DamageRadius);
Victims.TakeDamage(damageScale * DamageAmount,Instigator, Victims.Location - 0.5 * (Victims.CollisionHeight + Victims.CollisionRadius) * dir, (damageScale * Momentum * dir), DamageName);
}
}
bHurtEntry = false;
}
Hurt actors within the radius.

InterpoloateEnd
event InterpolateEnd( actor Other );
Called when Physics==PHYS_Interpolating and the next interpolation point has been reached.

IsA
intrinsic(303) final function bool IsA( name ClassName );
Return whether this actor belongs in a named class.

IsAnimating
intrinsic(282) final function bool IsAnimating();
Returns whether the actor’s mesh is currently animating.

KilledBy
event KilledBy( pawn EventInstigator );
Called when killed by another actor (killed just means dead, it doesn’t mean destroyed).

KillCredit
event KillCredit( Actor Other );
This actor has successfully killed another actor.

Landed
event Landed( vector HitNormal );
Ouch! That’s gotta hurt! This actor has transitioned out of PHYS_Falling.

LoopAnim
intrinsic(260) final function LoopAnim( name Sequence, optional float Rate, optional float TweenTime, optional float MinRate );
Loops the animation dequence forever. The AnimEnd() is called at the end of the sequence, and FinishAnim() calls return at the end of each iteration of the loop.

LostChild
event LostChild( Actor Other );
An actor owned by this actor has been destroyed or done a SetOwner() to another actor.

MakeNoise
intrinsic(512) final function MakeNoise( float Loudness );
Inform other creatures that you've made a noise they might hear (they are sent a HearNoise message). Senders of MakeNoise should have an instigator if they are not pawns.

Move
intrinsic(266) final function bool Move( vector Delta );
Moves the actor by the specified displacement vector, handling collision (based on the collision flags), Touch and Bump notifications.

MoveSmooth
intrinsic(3969) final function bool MoveSmooth( vector Delta );
Like move, but smoothly brushes against walls.

PlayAnim
intrinsic(259) final function PlayAnim( name Sequence, optional float Rate, optional float TweenTime );
Plays a named animation sequence in the actor’s Mesh once. The optional Rate scales the animation’s default rate. If a nonzero TweenTime is specified, the animation is first tweened from whatever is currently displayed to the start of the named animation sequence, before the animation sequence plays. When the animation playing completes, it stops and calls your optional AnimEnd() event and causes any latent FinishAnim() calls to return.

PlayerCanSeeMe
intrinsic(532) final function bool PlayerCanSeeMe();
PlayerCanSeeMe returns true if some player has a line of sight to actor's location.

PlaySound
intrinsic(264) final function PlaySound(sound Sound, optional ESoundSlot Slot, optional float Volume, optional bool bNoOverride, optional float Radius, optional float Pitch);
Play a sound effect.

PostBeginPlay
event PostBeginPlay();
Called immediately after gameplay begins.

PostTeleport
event PostTeleport( Teleporter OutTeleporter );
Called after this actor is teleported by a teleporter.

PreBeginPlay
event PreBeginPlay()
{
// Handle autodestruction if desired.
if( !bAlwaysRelevant && (Level.NetMode != NM_Client) && !Level.Game.IsRelevant(Self))
Destroy();
}
Called immediately before gameplay begins.

PreTeleport
event bool PreTeleport( Teleporter InTeleporter );
Called before this actor is teleported by a teleporter.

SetBase
intrinsic(298) final function SetBase( actor NewBase );
Sets the actor’s Base. A base of None means that the actor moves alone; setting the base to another actor in the world causes this actor to move and rotate along with its base. An example of using a base is standing on a moving platform.

SetCollision
intrinsic(262) final function SetCollision( optional bool NewColActors, optional bool NewBlockActors, optional bool NewBlockPlayers );
Changes the actor’s collision flags.

SetCollisionSize
intrinsic(283) final function bool SetCollisionSize( float NewRadius, float NewHeight );
Changes the actor’s collision size.

SetInitialState
simulated event SetInitialState()
{
if( InitialState!='' )
GotoState( InitialState );
else
GotoState( 'Auto' );
}
Called after PostBeginPlay.

SetLocation
intrinsic(267) final function bool SetLocation( vector NewLocation );
Teleports the actor to a new location.

SetOwner
intrinsic(272) final function SetOwner( actor NewOwner );
Sets this actor’s owner.

SetPhysics
intrinsic(3970) final function SetPhysics( EPhysics newPhysics );
Sets the actor’s current physics mode.

SetRotation
intrinsic(299) final function bool SetRotation( rotator NewRotation );
Sets the actor’s new rotation.

SetTimer
intrinsic(280) final function SetTimer( float NewTimerRate, bool bLoop );
Causes Timer() events every NewTimerRate seconds.

Sleep
intrinsic(256) final latent function Sleep( float Seconds );
Waits for a certain number of seconds to pass, then continues.

Spawn
intrinsic(278) final function actor Spawn(class<actor> SpawnClass, optional actor SpawnOwner, optional name SpawnTag, optional vector SpawnLocation, optional rotator SpawnRotation);
Spawn an actor. Returns an actor of the specified class, not of class Actor (this is hardcoded in the compiler). Returns None if the actor could not be spawned (either the actor wouldn't fit in the specified location, or the actor list is full). Defaults to spawning at the spawner's location.

Spawned
event Spawned();
Called when an actor is spawned during gameplay. Never called if the actor is preexisting when a level is loaded.

SpecialHandling
event Actor SpecialHandling(Pawn Other);
???.

TakeDamage
event TakeDamage( int Damage, Pawn EventInstigator, vector HitLocation, vector Momentum, name DamageType);
A certain amount of damage has been imparted on this actor.

Tick
event Tick( float DeltaTime );
Called each iteration of the game loop; varies depending on frame rate. DeltaTime indicates how much time has passed.

Trigger
event Trigger( Actor Other, Pawn EventInstigator );
Roy Roger’s house. ;-) This actor has been triggered by another actor Other, optionally caused by a pawn EventInstigator.

Timer
event Timer();
If you call SetTimer, this Timer function is called each time the timer has counted down from your time value.

Touch
event Touch( Actor Other );
This actor has begun touching (interpenetrating) another actor. Now, now, I know what you’re thinking, let’s keep it clean.

Trace
intrinsic(277) final function Actor Trace(out vector HitLocation, out vector HitNormal, vector TraceEnd, optional vector TraceStart, optional bool bTraceActors, optional vector Extent);
Traced a line and see what it collides with first. Takes this actor's collision properties into account. Returns first hit actor, Level if hit level, or None if hit nothing.

TravelPostAccept
event TravelPostAccept();
Called when carried into a new level, after AcceptInventory.

TravelPreAccept
event TravelPreAccept();
Called when carried onto a new level, before AcceptInventory.

TweenAnim
intrinsic(294) final function TweenAnim( name Sequence, float Time );
Tweens from whatever animation is currently being displayed, to the start of the specified animation sequence, then stops and calls AnimEnd() and releases any latent FinishAnim() calls.

UnTouch
event UnTouch( Actor Other );
This actor has stopped touching (interpenetrating) another actor.

UnTrigger
event UnTrigger( Actor Other, Pawn EventInstigator );
This actor has been untriggered by another actor Other, optionally caused by a pawn EventInstigator.

ZoneChange
event ZoneChange( ZoneInfo NewZone );
This actor has entered a new zone.

Copyright

Unreal™ © 1998 Epic Megagames, Inc. All Rights Reserved.


Object Class Reference

The Object Class

Written by Valiant - 11 June 1998

Copyright

Unreal™ © 1998 Epic Megagames, Inc. All Rights Reserved.

Version Information

Based upon Unreal Version 1.0. Please send comments to Valiant.

Class Definition

class Object
intrinsic;
Serves as the base class of all objects in Unreal.

Data Members

ObjectInternal
var intrinsic private const int ObjectInternal[6];
???

Parent
var intrinsic const parent;
The object's parent object, used for scoping purposes.

ObjectFlags
var intrinsic const int ObjectFlags;
The object's flags, described in the package documentation.

Name
var(object) intrinsic const editconst name Name;
The object's name.

Class
var(object) intrinsic const editconst class Class;
The object's class name.

Class Structure Definitions

BoundingBox
struct BoundingBox
{
var vector Min, Max;
var byte IsValid;
};
A bounding box, used for collision purposes.

BoundingVolume
struct BoundingVolume expands boundingbox
{
var plane Sphere;
};
A bounding box sphere together, used for collision purposes.

Color
struct Color
{
var() config byte R, G, B, A;
};
A color value structure.

Coords
struct Coords
{
var() config vector Origin, XAxis, YAxis, ZAxis;
};
An arbitrary coordinate system in 3d space.

DynamicArray
struct DynamicArray
{
var const int Num, Max, Ptr;
};
A dynamic array.

DynamicString
struct DynamicString
{
};
A dynamic string.

Guid
struct Guid
{
var int A, B, C, D;
};
A globally unique identifier.

Plane
struct Plane expands Vector
{
var() config float W;
};
A plane definition in 3d space.

Rotator
struct Rotator
{
var() config int Pitch, Yaw, Roll;
};
An orthogonal rotation in 3d space.

Scale
struct Scale
{
var() config vector Scale;
var() config float SheerRate;
var() config enum ESheerAxis
{
SHEER_None,
SHEER_XY,
SHEER_XZ,
SHEER_YX,
SHEER_YZ,
SHEER_ZX,
SHEER_ZY,
} SheerAxis;
};
Structure used for scaling and sheering.

Vector
struct Vector
{
var() config float X, Y, Z;
};
A point or direction vector in 3d space.

Class Constants

MaxInt
const MaxInt = 0x7fffffff;
Value of the maximum integer.

Pi
const Pi = 3.1415926535897932;
Value used for PI.

Class Intrinsic Operators and Functions

Bool Operators

!
intrinsic(129) static final preoperator bool ! ( bool A );
Logical negation operator

==
intrinsic(242) static final operator(24) bool == ( bool A, bool B );
Equality operator

!=
intrinsic(243) static final operator(26) bool != ( bool A, bool B );
Inequality operator

&&
intrinsic(130) static final operator(30) bool && ( bool A, skip bool B );
Logical AND operator

^^
intrinsic(131) static final operator(30) bool ^^ ( bool A, bool B );
??? if you know please email me the answer, TIA, Valiant

||
intrinsic(132) static final operator(32) bool || ( bool A, skip bool B );
Logical OR operator

Byte Operators

*=
intrinsic(133) static final operator(34) byte *= ( out byte A, byte B );
Multiplication operator

/=
intrinsic(134) static final operator(34) byte /= ( out byte A, byte B );
Division operator

+=
intrinsic(135) static final operator(34) byte += ( out byte A, byte B );
Addition operator

-=
intrinsic(136) static final operator(34) byte -= ( out byte A, byte B );
Subtraction operator

++
intrinsic(137) static final preoperator byte ++ ( out byte A );
Increment preoperator

--
intrinsic(138) static final preoperator byte -- ( out byte A );
Decrement preoperator

++
intrinsic(139) static final postoperator byte ++ ( out byte A );
Increment postoperator

--
intrinsic(140) static final postoperator byte -- ( out byte A );
Increment postoperator

Integer operators

~
intrinsic(141) static final preoperator int ~ ( int A );
One’s complement operator

-
intrinsic(143) static final preoperator int - ( int A );
Unary minus operator

*
intrinsic(144) static final operator(16) int * ( int A, int B );
Multiplication operator

/
intrinsic(145) static final operator(16) int / ( int A, int B );
Division operator

+
intrinsic(146) static final operator(20) int + ( int A, int B );
Addition operator

-
intrinsic(147) static final operator(20) int - ( int A, int B );
Subtraction operator

<<
intrinsic(148) static final operator(22) int << ( int A, int B );
Left shift operator, or, for vectors, forward vector transformation

>>
intrinsic(149) static final operator(22) int >> ( int A, int B );
Right shift operator, or, for vectors, reverse vector transformation

<
intrinsic(150) static final operator(24) bool < ( int A, int B );
Less than operator

>
intrinsic(151) static final operator(24) bool > ( int A, int B );
Greater than operator

<=
intrinsic(152) static final operator(24) bool <= ( int A, int B );
Less or equal operator

>=
intrinsic(153) static final operator(24) bool >= ( int A, int B );
Greater or equal operator

==
intrinsic(154) static final operator(24) bool == ( int A, int B );
Equality operator

!=
intrinsic(155) static final operator(26) bool != ( int A, int B );
Inequality operator

&
intrinsic(156) static final operator(28) int & ( int A, int B );
Bitwise AND operator

^
intrinsic(157) static final operator(28) int ^ ( int A, int B );
Bitwise exclusive OR operator

|
intrinsic(158) static final operator(28) int | ( int A, int B );
Logical negation operator

*=
intrinsic(159) static final operator(34) int *= ( out int A, float B );
Multiplication assignment operator

/=
intrinsic(160) static final operator(34) int /= ( out int A, float B );
Division assignment operator

+=
intrinsic(161) static final operator(34) int += ( out int A, int B );
Addition assignment operator

-=
intrinsic(162) static final operator(34) int -= ( out int A, int B );
Subtraction assignment operator

++
intrinsic(163) static final preoperator int ++ ( out int A );
Increment preoperator

--
intrinsic(164) static final preoperator int -- ( out int A );
Decrement preoperator

++
intrinsic(165) static final postoperator int ++ ( out int A );
Increment postoperator

--
intrinsic(166) static final postoperator int -- ( out int A );
Decrement postoperator

Integer functions

Clamp
intrinsic(251) static final function int Clamp ( int V, int A, int B );
Returns the first number clamped to the interval from A to B

Max
intrinsic(250) static final function int Max ( int A, int B );
Returns the maximum of two integers

Min
intrinsic(249) static final function int Min ( int A, int B );
Returns the minimum of two integers

Rand
intrinsic(167) static final Function int Rand ( int Max );
Returns pseudo-random integer from 0 to MaxInt

Float operators

-
intrinsic(169) static final preoperator float - ( float A );
Unary minus operator

**
intrinsic(170) static final operator(12) float ** ( float A, float B );
Exponentiation

*
intrinsic(171) static final operator(16) float * ( float A, float B );
Multiplication operator

/
intrinsic(172) static final operator(16) float / ( float A, float B );
Division operator

%
intrinsic(173) static final operator(18) float % ( float A, float B );
Modulus operator

+
intrinsic(174) static final operator(20) float + ( float A, float B );
Addition operator

-
intrinsic(175) static final operator(20) float - ( float A, float B );
Subtraction operator

<
intrinsic(176) static final operator(24) bool < ( float A, float B );
Less than operator

>
intrinsic(177) static final operator(24) bool > ( float A, float B );
Greater than operator

<=
intrinsic(178) static final operator(24) bool <= ( float A, float B );
Less than equal operator

>=
intrinsic(179) static final operator(24) bool >= ( float A, float B );
Greater than equal operator

==
intrinsic(180) static final operator(24) bool == ( float A, float B );
Equality operator

~=
intrinsic(210) static final operator(24) bool ~= ( float A, float B );
Approximate equality (within 0.0001), case insensitive equality

!=
intrinsic(181) static final operator(26) bool != ( float A, float B );
Inequality operator

*=
intrinsic(182) static final operator(34) float *= ( out float A, float B );
Multiplication assignment operator

/=
intrinsic(183) static final operator(34) float /= ( out float A, float B );
Division assignment operator

+=
intrinsic(184) static final operator(34) float += ( out float A, float B );
Addition assignment operator

-=
intrinsic(185) static final operator(34) float -= ( out float A, float B );
Subtraction assignment operator

Float functions

Abs
intrinsic(186) static final function float Abs ( float A );
Returns the absolute value of its argument.

Sin
intrinsic(187) static final function float Sin ( float A );
Returns the sine of its argument.

Cos
intrinsic(188) static final function float Cos ( float A );
Returns the cosine of its argument.

Tan
intrinsic(189) static final function float Tan ( float A );
Returns the tangent of its argument.

Atan
intrinsic(190) static final function float Atan ( float A );
Returns the arctangent of its argument.

Exp
intrinsic(191) static final function float Exp ( float A );
Returns the exponential of its argument.

Loge
intrinsic(192) static final function float Loge ( float A );
Returns the natural logarithm of its argument.

Sqrt
intrinsic(193) static final function float Sqrt ( float A );
Returns the square root of its argument.

Square
intrinsic(194) static final function float Square( float A );
Returns the square of its argument.

FRand
intrinsic(195) static final function float FRand ();
Returns a pseudo-random number equal to or between 0 and 1.0.

FMin
intrinsic(244) static final function float FMin ( float A, float B );
Returns the minimum argument.

FMax
intrinsic(245) static final function float FMax ( float A, float B );
Returns the maximum argument.

FClamp
intrinsic(246) static final function float FClamp( float V, float A, float B );
Returns the first number clamped to the interval from A to B

Lerp
intrinsic(247) static final function float Lerp ( float Alpha, float A, float B );
Returns the linear interpolation between A and B

Smerp
intrinsic(248) static final function float Smerp ( float Alpha, float A, float B );
Returns an Alpha-smooth nonlinear interpolation between A and B

String operators

$
intrinsic(228) static final operator(40) string[255] $ ( coerce string[255] A, coerce String[255] B );
String Concatenation

<
intrinsic(197) static final operator(24) bool < ( string[255] A, string[255] B );
Less than operator

>
intrinsic(198) static final operator(24) bool > ( string[255] A, string[255] B );
Greater than operator

<=
intrinsic(199) static final operator(24) bool <= ( string[255] A, string[255] B );
Less than or equal operator

>=
intrinsic(200) static final operator(24) bool >= ( string[255] A, string[255] B );
Greater than or equal operator

==
intrinsic(201) static final operator(24) bool == ( string[255] A, string[255] B );
Equality operator

!=
intrinsic(202) static final operator(26) bool != ( string[255] A, string[255] B );
Inequality operator

~=
intrinsic(168) static final operator(24) bool ~= ( string[255] A, string[255] B );
Aproximate equality (within 0.0001), or, for strings, case insensitive equality

String functions

Asc
intrinsic static final function int Asc ( string[255] S );
Returns the character code corresponding to the first letter in a string.

Caps
intrinsic(209) static final function string[255] Caps ( coerce string[255] S );
Capitalizes the string.

Chr
intrinsic static final function string[16] Chr ( int i );
Returns the character associated with the specified character code.

InStr
intrinsic(205) static final function int InStr ( coerce string[255] S, coerce string[255] t);
Returns the position of the first occurrence of one string within another.

Left
intrinsic(207) static final function string[255] Left ( coerce string[255] S, int i );
Returns a specified number of characters from the left side of a string.

Len
intrinsic(204) static final function int Len ( coerce string[255] S );
Returns the length of the argument.

Mid
intrinsic(206) static final function string[255] Mid ( coerce string[255] S, int i, optional int j );
Returns a string containing a specified number of characters from a string.

Right
intrinsic(208) static final function string[255] Right ( coerce string[255] S, int i );
Returns a specified number of characters from the right side of a string.

Object operators

==
intrinsic(114) static final operator(24) bool == ( Object A, Object B );
Equality operator

!=
intrinsic(119) static final operator(26) bool != ( Object A, Object B );
Inequality operator

Name operators

==
intrinsic(254) static final operator(24) bool == ( name A, name B );
Equality operator

!=
intrinsic(255) static final operator(26) bool != ( name A, name B );
Inequality operator

Class Member Functions

BeginState
event BeginState();
Remarks
Called immediately when entering a state, while within the GotoState call that caused the state change.

ClassIsChildOf
intrinsic(258) static final function bool ClassIsChildOf( class TestClass, class ParentClass );
Parameters
class TestClass
class ParentClass
Return Value
True if class is a child, else False.
Remarks
Returns whether one class is a subclass of another class.

Disable
intrinsic(118) final function Disable( name ProbeFunc );
Parameters
name ProbeFunc
Remarks
Disables a probe event.

DynamicLoadObject
intrinsic static final function object DynamicLoadObject( string[32] ObjectName, class ObjectClass );
Parameters
String[32] ObjectName
Class ObjectClass
Return Value
If the object is successfully loaded it is returned, else None.
Remarks
Tries to load an object from a file, and returns it.

Enable
intrinsic(117) final function Enable( name ProbeFunc );
Parameters
Name ProbeFunc
Remarks
Enables a probe event. The only functions which work with Enable and Disable are: Spawned, Destroyed, GainedChild, LostChild, Trigger, UnTrigger, Timer, HitWall, Falling, Landed, ZoneChange, Touch, UnTouch, Bump, BeginState, EndState, BaseChange, Attach, Detach, ActorEntered, ActorLeaving, KillCredit, AnimEnd, EndedRotation, InterpolateEnd, EncroachingOn, EncroachedBy, FootZoneChange, HeadZoneChange, PainTimer, SpeechTimer, MayFall, Die, Tick, PlayerTick, Expired, SeePlayer, EnemyNotVisible, HearNoise, UpdateEyeHeight, SeeMonster, SeeFriend, SpecialHandling, BotDesireability.

EndState
event EndState();
Remarks
Called immediately before going out of the current state, while within the GotoState call that caused the state change.

GetEnum
intrinsic static final function name GetEnum( object E, int i );
Parameters
Object E
Int i
Return Value
Name of the nth element of an enumeration.
Remarks
Returns the nth element of an enumeration.

GetPropertyText
intrinsic final function string[192] GetPropertyText( string[32] PropName );
Parameters
String[32] PropName
Return Value
String[192] value of the argument.
Remarks
Converts the value of an arbitrary variable to text.

GetStateName
intrinsic(284) final function name GetStateName();
Return Value
name
Remarks
Returns the name of this object’s current state, None if none.

GotoState
intrinsic(113) final function GotoState( optional name NewState, optional name Label );
Parameters
Optional name NewState
Optional name Label
Remarks
Sets the object’s current state, None means no state. If no label is specified, the Begin label is gone to.

IsInState
intrinsic(281) final function bool IsInState( name TestState );
Parameters
Name TestState
Return Value
bool
Remarks
Returns whether this object is in the specified state.

Localize
intrinsic static function string[192] Localize( name SectionName, name KeyName, name PackageName );
Parameters
Name SectionName
Name KeyName
Name PackageName
Return Value
String[192]
Remarks
Returns a localized (internationally translated) string from a package’s .int file.

Log
intrinsic(231) final static function Log( coerce string[240] S, optional name Tag );
Parameters
Coerce string[240] S
Optional name Tag
Remarks
Writes a message to the log file, usually \Unreal\System\Unreal.log.

ResetConfig
intrinsic(543) final function ResetConfig();
Remarks
Resets the values of the "config" variables to the originals in the Default.ini file.

SaveConfig
intrinsic(536) final function SaveConfig();
Remarks
Saves the current values of all "config" variables to the Unreal.ini file.

SetPropertyText
intrinsic final function SetPropertyText( string[32] PropName, string[192] PropValue );
Parameters
String[32] PropName
String[192] PropValue
Remarks
Sets the value of an arbitrary variable from text.

Warn
intrinsic(232) final static function Warn( coerce string[240] S );
Parameters
Coerce string[240] S
Remarks
Writes a script warning to the log file, including the current script and function.

Copyright

Unreal™ © 1998 Epic Megagames, Inc. All Rights Reserved.


Unreal Creature Care and Feeding Guide

Steven Polge
Epic MegaGames, Inc.
http://www.epicgames.com/

Audience: Level designers, programmers.
Last Updated: 07/01/98

AI Attributes

Typically, you will use the default values for the parameters below, with the following exceptions:

  • Tag, Orders, Ordertag,TeamTag, bTeamLeader, AlarmTag and sometimes Aggressiveness. AttitudeToPlayer, and Combatstyle.

Actor attributes menu:

  • OddsOfAppearing Likelihood that creature (or other actor) will appear in relevant game modes. Range is 0.0=Never to 1.0=Always (default is 1.0).

Display attributes menu:

  • DrawScale: If the drawscale of a creature is changed from its default value, its collision box (collisionheight and collisionradius) and health will be scaled by the new value at the start of gameplay.

Movement attributes menu:

  • JumpZ: How far a pawn can jump. If you don’t want a pawn to jump that can normally jump (for example, you want to keep a specific Skaarj up on a ledge), set its JumpZ to -1.

AI attributes menu:

  • bHasRangedAttack: specifies whether creature can attack from beyond melee range.
  • bMovingRangedAttack: specifies whether creature can use his ranged attack while moving (assuming creature supports this mode).
  • bCanStrafe: Specifies whether creature can strafe.
  • bFixedStart: If true, creature always starts from where it was placed. If false, then may start from alternate position depending on its orders (see below).
  • bQuiet: If true, makes less noise when wandering around
  • bTeamLeader: Leader of his team (as defined by the TeamTag). There should be only one per team.
  • bIgnoreFriends: Default false. If true, creature doesn’t react to friend's noises.
  • bDelayedPatrol: If true, and creature’s orders are Patroling, then the creature will begin his patrol only when it is triggered. However, it will break off this patrol and react
  • to the player if it sees him. To force the creature to continue to his destination, you can also specify the patrol destination in the creature’s alarmtag.
  • bHateWhenTriggered: Creature will change its attitude to hate the player when triggered by the player.
  • TeamTag: Creature team to which this creature belongs. As long as the team leader is alive, creatures on the same team will coordinate with each other.. If the species can speak, then creatures on the team will talk to each other.
  • Skill: Added skill of this creature. Default is 0. For example, if Skill = 1, then the creature will act like a difficulty 2 creature when the difficulty setting = 1.
  • SightRadius: Maximum sight distance. Creature’s likelihood of seeing other pawns depends on how far away the other pawn is relative to the creature’s sight radius. the other pawn’s visibility, and (soon) how well lit the other pawn is.
  • PeripheralVision: The cosine of the maximum angle of peripheral vision. A creature which can see 90 degrees to each side (180 degrees total) would have PeripheralVision = ?.
  • HearingThreshold: Minimum scaled noise loudness which can be heard. While the exact hearing range depends on the creature’s alertness, a good rule of thumb is that creatures can hear a typical loud noise if there is a line of sight to the noise location, and HearingThreshold < 200 / (distance to the noise in Unreal units). For example, a creature with HearingThreshold = 0.4 could typically hear noises if they were no more than 500 units away. A hearing threshold of 0.2 translates to a max hearing distance of 1000 units. Creatures can never hear noises which are more than 2800 units away.
  • Orders: See Orders section below.
  • OrderTag: See Orders section below.
  • TimeBetweenAttacks: Time in seconds between ranged attack barrages (which may include multiple shots).
  • MeleeRange: Maximum distance from target (not including collision radius of this creature or its target) to launch a melee attack.
  • Aggressiveness: Typically in the range 0.0 to 1.0. Primarily affects attitude changes (more aggressive creatures will continue to fight, even against a stronger player, less aggressive creatures will be more quick to flee). May also have an effect on tactical combat decisions. Note that creatures will only run away if they have a home base (except for Nali and cow)
  • Visibility: How visible is this pawn to other creatures (note - does not affect rendering!) 0 = invisible, 128 = normal, 255 = highly visible.
  • AttitudeToPlayer: Initial attitude to player (may change depending on player’s actions, and creature's aggressiveness.
    • ATTITUDE_Fear - creature will try to run away
    • ATTITUDE_Hate - creature will attack player
    • ATTITUDE_Frenzy - Indiscriminate, berserk attack (no fear, lower accuracy)
    • ATTITUDE_Threaten - Creature will assume a threatening posture
    • ATTITUDE_Ignore - Creature will ignore player
    • ATTITUDE_Friendly - Creature will fight player’s enemies
    • ATTITUDE_Follow - Creature will follow player (not yet implemented).
  • Intelligence: Creature’s intelligence
    • BRAINS_None - amoeba like - only reacts to immediate stimulus
    • BRAINS_Reptile
    • BRAINS_Mammal
    • BRAINS_Human
  • ReFireRate: Likelihood of firing again immediately after a ranged attack (values from 0 to 1 with higher values indicating a higher probability).
  • CombatStyle: Tactical decision making during combat. Value between 0.0 and 1.0 Higher means the creature is more likely to charge, try to melee, etc.
  • AlarmTag: If AlarmTag is set, when the creature sees the player, it will (at all costs) first go to the actor with the same Tag as its AlarmTag. Friendly creatures will wait for the player as they move towards this actor. This can be used either to have friendly creatures lead the player to some area or object, or for unfriendly creatures to try to trigger events or set up initial attack formations. For example, you could have three Kraal in a room. Two attack you, while the other runs for a button which will lock the door behind you and sound an alarm. Alternatively, you could have them spread out in formation, seeking cover or advantageous positions when they see you. AlarmPoints (see below) are designed to provide extensive scripting control when used with the AlarmTag.
  • SharedAlarmTag: For each group of creatures who have the same string in this field, one of them will choose it to be his alarmtag (overriding any existing alarmtag). You can use this to provide some randomness in which creature performs some important action.
  • DropWhenKilled: Creature will drop one of these when it is killed.
  • FirstHatePlayerEvent: Creature will trigger this event the first time he decides to attack the player.

Orders

The following initial orders can be given to all creatures. After killing an enemy, they will attempt to continue to execute these orders. Some orders (patroling, guarding) are associated with other actors in the level. For these, you should set the creature’s OrderTag to the tag of the associated actor.

Waiting

The default orders for all creatures. Creature will wait in place, playing various waiting animations, until it receives some external stimulus (sight or sound).

Patroling

Patroling creatures will patrol along a set of Patrolpoints (a subclass of NavigationPoint) specified by the level designers. The creature’s OrderTag should contain the tag of the first Patrolpoint in its patrol. When a creature reaches a Patrolpoint, it will pause for the PauseTime specified in the patrol point, facing in the direction the Patrolpoint is oriented. While pausing, it will play waiting/patrolstop animations. If a PatrolAnim is specified, the creature will play that specific animation the number of times specified by NumAnims. Each time it plays the animation, it will also play the PatrolSound if one is specified. Note that the amount of time creatures playing a PatrolAnim pause depends on how long it takes to play the animation NumAnims times, and not the pause time. However, the PauseTime must still be set to a positive number. It will then continue to the Patrolpoint specified in the current Patrolpoint’s NextPatrol. If a creature cannot reach the next patrol point, it will wait at its current position. If bFixedStart is false, the creature will start at some random point along his patrol route (not yet implemented).

Guarding

Guarding creatures will guard the actor whose tag equals their OrderTag. They will remain near, and try to return the guarded actor after combat. If their initial attitude is ATTITUDE_Threatening, they will threaten the player, and try to remain between the player and the guarded object. If the player touches the object they will attack.

Ambushing

The creature is waiting, but more alert than in the waiting state (and possibly playing different animations). If bFixedStart is false and there are Ambushpoints (a subclass of Navigationpoint) with the same tag as the creature, the creature may randomly pick one of these as its starting point. The orientation of the Ambushpoint determines the orientation of a creature using it. Ambushpoints may also be used by creatures that decide to ambush rather than pursue a player.

Swarm AI Pawns

Horseflies, biterfish, and blobs all use variations of swarm AI. Do not place individual Horseflies and biterfish in your map. Rather, place horseflySwarms and biterfishSchools, and specify in their properties how many individual creatures they should contain. For blobs, add a parentBlob and a number of bloblets, and give them all the same tag.

PathNode placement

You can place pathnodes manually, or you can use PATHS BUILD LOWOPT from the console to get a first pass placement done automatically. In any case, you must do a PATHS DEFINE from the console to create the connections between pathnodes.

To use the navigation system, a creature must first choose the best nearby node to "get on" the network. For performance reasons, creatures limit their search for path nodes to those within an 800 unit radius (see FSortedPathList::FindVisiblePaths in UnRoute.cpp). This is because the test to determine if a creature can reach a path is very expensive. However, once creatures are "on the network", travelling from pathnode to pathnode, they use the reachspec tables in each pathnode to determine reachable nodes. There is no distance limit for reachspecs, so paths may be defined between two pathnodes which are any arbitrary distance apart. For performance reasons, paths between distant pathnodes which have a suitable alternate
path using intermediate path nodes (with a low added cost) are culled from the path network.

I recommend that paths should be less than 700 units apart, so that creatures don't ever start off going in the wrong direction to get on the network. In addition, paths on stairs and ramps should be closer together (<350 apart) Given that, you should use the minimum number of paths needed to cover the area for which you want intelligent navigation. Note that all navigation points are equally used as paths, so there is no need to place pathnodes redundantly near patrolpoints, etc..

Paths should in general be placed at intersections, making sure that they have the maximum visibility in all potential directions. Paths need a line of sight to each other, clear to the width of the maximum width (2 * collisionradius) of creatures which will use this path. You should also place paths on ledges, with a line of sight to paths below, so that creatures can intelligently navigate jumping off ledgest. Vertical line of sight has no extent requirement.

In UnrealEd, look at the Properties menu in the map view window. There is an option to show the path connections. These are best looked at using the overhead view. Note that a connection between two Navigation points indicates that at least some creatures can use this path. However larger creatures, or creatures which do not support certain movement modes (e.g. the bCanJump, bCanSwim, etc. attributes of pawns) may not be able to use these paths.

Thing Factories and Creature Factories

Creature and thing factories may be triggered by a trigger whose event equals the creature factory’s tag, or will trigger themselves if a player touches it. Since zone changes can cause an event, you could for example cause a creature factory to start producing razorfish as soon as a player enters the water. The first creature spawns instantly. Creature and thing factories use Spawnpoints ( a type of navigation point) as the locations to create actors. Up to 16 spawnpoints can be associated to a given factory. Spawnpoints and creature factories are associated by giving them the same tag. If a Spawnpoint has a tag in its event field, that event will occur when a creature is spawned from that spawnpoint. By default, creature factories are covert, which means they will only spawn actors at a spawnpoint no player can see. A creature from a factory has its event set to the factory’s tag (so its death will trigger the factory). Thing and creature factory properties are:

  • prototype: The class of actors produced by this factory.
  • maxitems: The maximum number of active actors from this factory at any time. If it is a creature factory, new creatures will only produced either initially, or when a creature dies, to get back up to maxitems until the factory capacity is reached.
  • Capacity: The maximum number of actors this factory can ever produce (-1 = no limit). After reaching this limit, the factory shuts down.
  • AddedCoopCapacity: Extra items to produce if the game type is CoopGame.
  • Interval: The average time interval between spawns.
  • timeDistribution: The distribution of the interval between spawns.
    • DIST_Constant - constant interval (always equal to Interval).
    • DIST_Uniform - uniform interval (random time between 0 and 2 x interval)
    • DIST_Gaussian - Gaussian distribution, with mean = interval
  • bOnlyPlayerTriggered: Only a player can trigger this factory (true by default).
  • bCovert:; Only do hidden spawns (true by default for Creature factories, false by default for thing factories).
  • bStoppable: Stops producing when untouched.
  • Orders: (Creature factory only) Creatures from this factory will have these orders
  • OrderTag: and ordertag. By default they will attack the instigator of the factory.
  • ItemTag: Tag given to items produced by this factory.
  • AlarmTag: (Creature factory only) AlarmTag given to creatures produced by this factory.

Navigation Points

All navigation points have a bPlayerOnly attribute. If set to true, it means that only Players (bots) will use this path for navigation. Its not perfect, but if you put two in a row along a hallway with turns, it will probably keep the creatures from going through that hall. Exceptions are if the creature is close behind a player and in hot pursuit, or if he is already very close to the path when he tries to use navigation.

Homebase

Homebase of creatures whose tag is the same as the Homebase actor. Creatures who fear the player will flee to their homebase if they have one. There, they will turn and fight. When a creature reaches its homebase, it will face in the direction specified by the homebase’s orientation. Also, it will check for nearby ambushpoints whose tag = the creature’s tag, and if any are found will go to them.

Ambushpoint

See Ambushing orders.

InventorySpot

Automatically inserted during PATHS DEFINE to identify location of inventory (for bots.

Teleporters

PlayerStart

SpawnPoint

See Thing and Creature factories.

PatrolPoints

See Patroling orders.

LiftCenter

Place this Navigation Point at the spot on a lift you want the creature to stand while using the lift. Its LiftTag should be set to the tag of the lift it is on.

LiftExit

LiftExits should be place at the spots you want creatures to wait before getting on a lift. Their LiftTag should be set to the tag of the lift they are associated with.

AlarmPoints

Alarmpoints are designed to be used in conjunction with the alarmtag to cause creatures to perform complex actions upon seeing a player. If the Alarmpoint’s event is set, it will trigger all creatures with that tag when the alarm creature reaches the Alarmpoint. The attributes are:

  • NextAlarm: The next alarmpoint to go to after this one. If none, the creature will revert to normal AI after completing the actions specified by this point.
  • PauseTime: How long to pause at this alarmpoint.
  • DuckTime: How long to pause after playing the AlarmAnim (only used if an AlarmAnim is specified). Its called ducktime, because a typical use would be to have the creature duck, wait for ducktime, and then stand up and shoot (if bAttackWhilePaused is true). If you want to the creature to track the player while ducking, be sure to set bAttackWhilePaused.
  • AlarmAnim: If specified, the creature will play this animation upon reaching the alarmpoint, as well as the AlarmSound if specified.
  • bStrafeTo: If true, and the creature can strafe, then the creature will strafe to this AlarmPoint, facing the enemy player. If the creature has a moving attack, it will fire while strafing if it has a clear shot.
  • bAttackWhilePaused: If true, the creature will shoot at the player while paused (unless he has a ShootTarget). If the player is not visible, the creature will fire at where he last saw the player.
  • bNoFail: If true, the creature will never give up trying to get to this Alarmpoint (just as it never gives up trying to get to other alarmtag actors). If false, and the creature gets knocked off its path, it may give up if it can no longer easily get to the alarmpoint.
  • bStopIfNoEnemy: If true, and the enemy is not visible, creature will stop at this Alarmpoint until it sees the enemy again. Otherwise, it will continue progessing through the Alarm actions.
  • bKillMe: Any creatures that are triggered by this alarmpoint (their tag = the alarmpoint’s event) will hate and kill the triggering creature, even if they wouldn’t normally.
  • bDestroyAlarmTriggerer When a creature reaches this alarmpoint (and it is the creature’s alarmtag destination), the creature is immediately destroyed.
  • ShootTarget: If bAttackWhilePaused is true and ShootTarget has the tag of some actor, the creature will fire at that actor rather than the player.
  • AlarmSound: If an AlarmAnim is specified, the AlarmSound will be played in conjunction with the AlarmAnim.

Physics Notes

Creatures may be able to walk over any opening up to 3X their CollisionRadius. If you want something to definitely fall in, or not be able to cross without jumping, make it bigger than this. Also, make sure that most hallways, and particularly intersections and corners are at least (4 x CollisionRadius + 10) of the largest creature you want to navigate it (so that two creatures won’t get hung up with each other. Ideally, hallways should be at least 6x the CollisionRadius of the largest creature which may navigate it. Also WarpZones should be at least 70 units deep ideally to allow the AI to handle them properly.

Triggering creatures

Creatures that have not engaged an enemy can be triggered by a trigger whose event equals the creature’s tag. Creatures that are triggered will make whatever instigated the trigger their enemy. You can use this to have creatures ignore the player until a certain event happens, such as crossing some threshold (using ATTITUDE_Ignore) - if bHateWhenTriggered is true, the creature will change its attitude to hate the instigator. Creatures will also trigger their event when they die. You can use triggers in conjunction with bDelayedPatrol to cause a creature to start a scripted set of actions when triggered by the player (without noticing the player).

Triggers

Two new trigger types have been added. TT_PawnProximity will allow any pawn, not just players to use the trigger. TT_ClassProximity will only allow actors of the class ClassProximityType to use the trigger. For example, you might make a certain switch only operable by a Nali. Also, triggers with type TT_Shoot now also have an optional DamageThreshold value. This is the minimum damage required for triggering (instantaneous, not cumulative). The optional attribute ReTriggerDelay specifies the minimum time before a trigger can be triggered again.

RepeatTriggerTime, if set to a value greater than zero, causes the trigger to send out its event at an interval of RepeatTriggerTime seconds while the triggering actor remains within the trigger’s radius.

EarthQuake

The EarthQuake keypoint can be triggered to shake the player’s view for a specified duration, as well as possibly tossing players around. It has no effect on other actors. To simultaneously toss around other actors, use the ThrowStuff keypoint. Earthquake attributes are:

  • magnitude: Specifies the amount to roll the player’s view, and the amount to throw him if bThrowPlayer is true. A magnitude of 2000 is a pretty strong quake.
  • duration: Time in seconds quake lasts.
  • radius: Radius for which players are affected. A player who goes beyond the radius will continue to have his view shaken for the duration of the quake, but he will no longer get thrown around.
  • bThrowPlayer: If true, players are thrown around during the quake.

ThrowStuff

The ThrowStuff keypoint can be used to cause actors whose tag matches the ThrowStuff keypoint’s event to be tossed using the vector specified in throwVect. If Numthrows is larger than 1, the actors will be thrown again after the specified interval. If bRandomize is true, the throwVect will be randomly altered for every toss. Having Numthrows > 1 and bRandomize can be useful for making stuff get tossed around during an earthquake. Other potential uses for a ThrowStuff keypoint include launching a creature in a specified direction when triggered by the player, or turning the physics on of actors when a brush they are on tips and you want them to slide/fall off (in this case, use a ThrowVect of (0,0,0), and they will just be set to falling).

MakeNaliFriendly

When triggered, this keypoint makes all Nali in the level friendly. This is useful for cases when the player may have scared the nali previously, or to set up situations where the Nali will reward the player after he does something (such as killing a skaarj which was threatening the Nali).

Jumper

When triggered, this trigger makes the triggering creature jump in the direction it is pointing. It can be limited to trigger once only, or to only affect a certain class of creatures. The Jumper trigger has a JumpZ variable which specifies how high the triggered creature should jump (giving you control over where it lands).

FearSpot

This trigger causes creatures to avoid an area (not perfectly).If you use this, make sure there are also no paths going through the area you want them to avoid. The FearSpot keypoint has a bInitiallyActive attribute, which can be turned on and off by triggering it.

Movers

Two new booleans have been added to Movers. bUseTriggered indicates that the brush is activated by a player doing a use (exec grab) on it. BDamageTriggered indicates that damage to the brush will trigger it. The optional attribute DamageThreshold specifies the minimum (instantaneous, not cumulative) damage needed to trigger a bDamageTriggered brush.

Mover bump types are now supported, specifying which types of actors can trigger a mover by bumping it (for movers which are activated by bumping).

By default, all the movers with the same tag will return or stop if one returns or stops because of encroachment. To prevent all the movers with the same tag from acting this way, you can divide them into sub-groups using the ReturnGroup attribute.

Special Creature Features

Mercenary

The mercenary has several special sets of idle animations accessible through the following attributes - bButtonPusher, bTalker, and bSquatter. If one of these attributes is set true, then these animations will play whenever the mercenary is stopped (waiting, or stopped on patrol, etc.). Note that for bTalker and bSquatter, the mercenary should be talking to teammates (so that the conversation will be synchronized).

bHasInvulnerableShield, bCanFireWhileInvulnerable, and invulnerableCharge are parameters which can be used to adjust the Mercenary’s invulnerability capabilities.

Skaarj

The Skaarj has a special set of animations accessible through the attribute bButtonPusher, which work in an analogous fashion to the mercenary bButtonPusher animations. Also, bFakeDeath tells the skaarj to remain laying down until a little after he sees the player. Note that skaarj using bFakeDeath should be placed slightly above the floor, so they don’t adjust out of the floor on first seeing the player (thereby giving away the fact that they are not really a carcass).

Krall

The Krall has a special set of animations accessible through the attribute bDicePlayer. Krall using these animations should be teamed with several other Krall and closely arranged in a circle. The bSleeping attribute will make a Krall play a sleeping animation and be very un-alert while waiting.

Nali

A Nali with orders "Guarding" will play his praying animations. A Nali with orders "Ambushing" will play his meditating animation.

The bNeverbow attribute should only be used with Nali who have a homebase (and only in well tested situations). When enabled, the Nali will not stop and bow while retreating.

Birds

Although birds are flockpawns, they are added individually. They have several different selectable behaviours. By default, they will fly around randomly in an area whose size is

determined by their CircleRadius attribute. Note that this area should be relatively free of obstructions, as birds are not smart. If bCircle is set, they will soar around a circle with radius CircleRadius. If GoalTag is set, they will not move until triggered, and then quickly fly to their GoalActor. Birds are now unlit by default. If you want a bird to show off cool light/shadows, then set its bUnlit property to false. Birds that you don’t see no longer cost anything. However, be sure not to use birds in big open areas where they can be seen from far away, as the full cost of rendering their mesh still exists, even if they are smaller than a pixel.

Cow and Rabbit

The bStayClose attribute (on by default) specifies whether the creature will wander around within its WanderRadius, rather than wandering randomly anywhere.

Pupae

Pupae can be placed and rotated onto any wall or ceiling initially. When they see the player, they will drop off the wall/ceiling and go at the player.

Brute

The bTurret attribute when true will make the Brute stay in place and just fire at the player.

Warlord

The Mutilating orders are for hub one. The Warlord has a new attribute, bTeleportWhenHurt, which causes him to teleport away rather than dying.

BiterFishSchool

The bNonAggressive attribute when true will make the fish ignore the player. BiterFishSchools have a fishcolor attribute. 0 to 6 makes all the fish in the school the same color, while a value larger than 6 gives them a random mix of colors.

Carcasses

You can add permanent creature and player carcasses (they are a subclass of decoration). If a creature has multiple death animations, you can select the death pose by changing AnimSequence in the Display properties.

Carcass management note: Carcasses are destroyed more quickly in zones that already have reached their limit of carcasses. In less cluttered zones, carcasses will never disappear or change forms before your eyes. The MaxCarcasses attribute of zones sets the limit for "uncluttered" carcasses in a zone. The default is now 3, which should be optimal for most situations.

Zones

The ZoneTag is used to tag a zone which is to be triggered by a ZoneTrigger. WarpZones can be triggered to sequence through an array of destinations specified in their properties. Other zones will toggle bPainZone when triggered. The ZoneFog and ZoneFlash properties in ZoneLight allow you to change the player’s display color/fogging for a given zone.

The ZoneTerminalVelocity attribute changes the maximum falling speed of players in the zone.

There are now also EntrySound, EntryActor, and ExitSound attributes for zones. Zones with bWaterZone set will play these as pawns enter/leave the zone. These are modified for special water zones, such as lava or slime.

When a player enters a TeleporterZone, he is teleported by the associated teleporter (specified by the TeleporterZone’s TeleporterTag).

Screen Flashes

There are two components used to create screen flashes. ViewFog is used to color the screen. It is a vector whose components are RGB values between 0 and 1. As ViewFog increases the brightness of the screen, you need to use ViewFlash to keep it from over-brightening.

ViewFlash is a vector whose components should be between 0 and –1 (for example (-0.5, -0.5, -0.5). It reduces the brightness of the screen. Typically, all the components will have the same value, which should have a magnitude about the same as the average magnitude of the ViewFog components to keep the screen from changing brightness.

AI Difficulty Differences

Write this.

Scripted Execs and Cheats

Game Commands

  • FeignDeath pretend to be dead
  • Grab Pick up a decoration or "use" a mover
  • Say "xxx" Send message "xxx" to all players
  • RestartLevel
  • ThrowWeapon Toss out your current weapon.
  • Kick "name" Kick player named "name" off of server (only from bAdmin)
  • Pause
  • SwitchWeapon n Switch to weapon in weapon group n
  • Fire
  • AltFire
  • Jump
  • Suicide
  • AlwaysMouseLook b If b=1, always mouse look (and turn off SnapToLevel and
  • LookUpStairs. If b=0 turn off AlwaysMouseLook.
  • SnapView b If b=1, snap view back to horizontal when MouseLook is unpressed (and turn off AlwaysMouseLook). b=0 turns it off.
  • StairLook b If b=1, turn on automatic view orientation up and down stairs/slopes.
  • SetDodgeClickTime f Modify the dodgeclicktime (time between button presses for dodge move. F must be less than 0.3. A negative value disables the dodge move.
  • SetName "xxx" Change your name to "xxx".
  • SetTeam "xxx" Change your team to "xxx".
  • PlayersOnly Only players allowed in the level.
  • ViewPlayer "xxx" View the game from the point of view of the player named "xxx".
  • The view is only changed if you are allowed to see through this player’s eyes (e.g. a teammate in team play or coop mode).
  • SetAutoAim f Adjust autoaim. Setting it to 1.0 disables it.
  • SetHand xxx Set weapon hand to xxx= left, right, or center.
  • Taunt xxx Play the taunt animation xxx (wave, victory1, taunt1)
  • NeverSwitchOnPickup b Default is 0. If set to 1, then you never automatically switch to a picked up weapon.
  • PrevWeapon Switches to the next lower priority weapon.
  • NextWeapon Switches to the next higher priority weapon.
  • InvertMouse b Default is 0. If set to 1, then your mouselook is inverted.
  • ShowScores Toggles the display of the scoreboard in multiplayer games.
  • ShowMenu Cause the menu to be displayed.
  • SetSensitivity f Changes the mouse sensitivity scaling to f (a float).
  • SetBob f Changes the bob value to f (default is 0.016).
  • AddBots n Adds n bots to the level (game must be a DeathMatchGame or Subclass).

Cheats

  • ViewClass xxx View the game from the point of view of a member of class "xxx". Successive executions of this function will iterate through the class. For example, ViewClass class’SkaarjWarrior’ will show you the game from the point of view of a Skaarj in the level, if there is one.
  • KillAll xxx Kills all actors of class xxx (including subclasses)
  • Amphibious Makes you amphibious (no limit to underwater time).
  • Fly
  • Walk
  • Ghost
  • ListInv Print a list of your inventory items to the log.
  • AllAmmo Gives you 999 ammo for all the weapons you have.
  • Invisible b b=1 makes you invisible. b=0 makes you visible.
  • TempStatus b b=1 turns the temporary status bar ( at the top of your screen) on. b= 0 turns it off.
  • God Toggles god mode.
  • BehindView b
  • SloMo f Changes the rate of time in the game. 1.0 is normal speed. 2.0 is twice normal speed. 0.2 is one fifth normal speed…
  • Players Prints a list of players and their current score.
  • JumpZ Changes your jumping height (260 is default).
  • KillPawns Removes all pawns from the game.
  • CheatAdd class’xxx’ Adds an actor of class ‘xxx’ right in front of you. This can be anything (a weapon, ammo, a creature, etc.), as long as an actor of that class exists or is referenced in your level.
  • RememberSpot Remember your current location (see ShowPath below).
  • ShowPath Show recommended path back to location remembered by
  • RememberSpot. Displays a beacon. When you touch that beacon, it shows the next one.
  • SetDesiredFOV f Sets your field of view to f (a value between 1 and 179).

End


UT Bot AI and Gameplay

Unreal Tournament AI and Gameplay for Level Designers

Steven Polge
Epic MegaGames, Inc.
http://www.epicgames.com/

Audience: Level designers, programmers.
Last Updated: 11/30/99

Porting Unreal I maps to Unreal Tournament

Unreal I content is automatically replaced by Unreal Tournament content if you run an Unreal I DM map with one of the Unreal Tournament gametypes. However, we recommend that you manually replace the Unreal I inventory items in your level with their UT equivalents and release an updated version of the map. Otherwise, both the Unreal I and UT versions will be loaded on the content, which will cause swapping on clients with low amounts of memory.

The Navigation Network

Creating the Navigation Network

Unreal Tournament bots use a system of waypoints to navigate the level. Using waypoints allows UT to minimize the CPU cycles used by the AI, and gives level designers maximum control over how the AI navigates their levels.

These waypoints are defined by placing actors which are subclassed from NavigationPoint in the level. This includes actors such as PlayerStarts and Teleporters which designers will place to make their level playable. Certain NavigationPoints, such as InventorySpots and WarpZoneMarkers are automatically placed when the designer "defines" the paths (described below). Other NavigationPoints are intended for Unreal I style single player situations and are not used at all in UT. These include AlarmPoints, ButtonMarkers, PatrolPoints, QueenDests, SpawnPoints, Transporters, and TriggerMarkers.

Once the level geometry is complete, the level designer should fully map the level into the navigation network using PathNodes. Place PathNodes at intersections and turns so that there is as much clearance as possible from corners (at least 50 world units if possible). Paths need a line of sight to each other, clear to the width of the maximum width (2 * collisionradius) of creatures which will use this path. PathNodes should not be placed more than 700 units apart. Since PlayerStarts and Inventory will be placed on the navigation network, the designer can (and should) not place PathNodes where these other NavigationPoints can substitute. On stairs and ramps, the PathNodes should only be 350 units apart. The objective should be to use a minimum number of PathNodes to completely cover a level.

Version 402 of Unreal Tournament has an automatic PathNode generator which will do part of the work of placing paths automatically. It will attempt to cover the level with paths, but will not place special NavigationPoints such as playerstarts, liftcenters, etc.. To use the automatic PathNode generator, open your level in UnrealEd. Select the Window menu, and from it the log. At the prompt type 'Paths Build' (no quotes). This will take 5 to 10 minutes for a typical level.

To navigate around the level, bots will first find the nearest reachable NavigationPoint, and move to it. They will then move toward their destination, going from NavigationPoint to NavigationPoint. It is important that some NavigationPoint is reachable (there are no obstructions in the way) from any location a bot may reasonably get to in the level. If a bot can't find a valid path, either because it can't find a way to get on the network, or there is no valid way to get from the network to its destination, or the path on the network is broken, then the bot will wander (walking) around its current area.

The final step in setting up the navigation network is performing a PATHS DEFINE. This can be done by typing PATHS DEFINE into the UnrealEd console, or pressing the "Paths Define" button in the lighting section of the rebuild window. The designer can examine the network generated in any of the UnrealEd world view windows by selecting "Show Paths" from that window's "View" menu. NavigationPoints which are successfully connected will have either blue or red lines between them. Bots will use either blue or red paths, but blue paths are preferable - they indicate a cleaner, more obstruction free path. Each time geometry is changed or NavigationPoints are added or moved (including inventory, since paths are generated from inventory items), a PATHS DEFINE should be performed.

There are several common problems that result from assumptions and approximations made by the AI to minimize CPU overhead. Make sure that all NavigationPoints are separated by at least 50 world units. If two pathnodes are too close together, a bot may get stuck on them. The editor log window will display warnings during a PATHS DEFINE if paths are too close together. This problem commonly occurs with InventorySpots, which are automatically placed near all inventory items. Don't place PathNodes too near to inventory.

In some situations, bots may get stuck on the edge of a ledge where it runs into a wall. This occurs because of a threshold problem. Bots won't jump off ledges until the angle of their destination is greater than 45 degrees from the ledge direction. If they run into a wall right around this threshold, they may get confused. The solution is to adjust the NavigationPoint they were trying to reach, or add an intermediate PathNode.

Bots will have problems if the NavigationPoints are too high off the ground. Make sure that the NavigationPoint is no more than 1.5x the bot's collisionheight off the ground. This will normally be the case automatically, but in a few situations (such as very steep ramps), the level designer may need to manually adjust the Z position of the NavigationPoint.

PathNodes for the bots to use while swimming must be in the water. Placing paths above the water's surface will result in bots bobbing at that spot, attempting unsuccessfully to levitate to the PathNode's location.

WarpZones should be at least 70 units deep to allow the AI to handle them properly.

Adding Support for Lifts

There are two special NavigationPoint classes used to help bots understand how to use lifts, LiftCenters and LiftExits. Give the lift a unique tag. Place a LiftCenter on the lift, and set its LiftTag attribute to be the tag of the lift. At each exit from the lift, far enough away so that bots standing at that spot won't interfere with the lift, add a LiftExit. Each LiftExit should have its LiftTag set to the tag of the lift.

The default state for movers is BumpOpenTimed. Do not use this state for lifts! Bots misinterpret movers that are BumpOpenTimed as being doors. Instead, use the StandOpenTimed state for most lifts. Bots will also understand triggered lifts (TriggerOpenTimed, TriggerToggle, etc.). In many cases, they will use triggered lifts without any further help. However, for certain complex situations, such as when a delayed dispatcher is used to control the lift, bots may need a hint. In these cases, set the LiftCenter's LiftTrigger attribute to the tag of the trigger the bot should use to control this lift.

There are two other configurable LiftCenter attributes of interest. These should be modified only if absolutely necessary. MaxZDiffAdd, which defaults to 0, specifies an additional allowable difference in height between the bot and the LiftCenter for the bot to consider that LiftCenter reachable. This is useful if the bot must stand below the lift (down stairs or a ramp) while waiting for it. MaxDist2D is the maximum distance between the bot and the LiftCenter. It defaults to 400 world units.

NavigationPoint Attributes

The only configurable NavigationPoint attribute that should be modified is bOneWayPath. bOneWayPath can be set to true so that paths will only be valid in the direction the NavigationPoint is facing. Turn on the bDirectional attribute of the NavigationPoint to adjust the direction it faces.

Special Purpose NavigationPoints

Several special purpose NavigationPoints exist to provide tactical or game specific positions or to provide support for special movement modes such as using translocators and jumpboots. Game specific NavigationPoints will be described in the section of this document devoted to that game type.

PlayerStarts specify allowed spawning locations for players and bots. There are two relevant configurable attributes in PlayerStarts. The TeamNumber attribute specifies which team is allowed to spawn at that PlayerStart in teamgames where team specific PlayerStarts are enforced. The bEnabled attribute, which is true by default, can be toggled to enable or disable the PlayerStart by triggering the PlayerStart.

AmbushPoints are used to specify good camping spots. They are directional and should be pointed in the direction the bot should be looking while using that AmbushPoint. AmbushPoints have two configurable attributes, SightRadius and bSniping. SightRadius specifies how far out the bot should be looking for enemies when camped at the AmbushPoint. bSniping specifies that bots using this ambushpoint should stay at this point and snipe after acquiring an enemy, rather than chasing after the enemy.

DefensePoints are a subclass of AmbushPoint used in team games. DefensePoints have three additional configurable attributes, Team, Priority, and FortTag. The Team attribute specifies which team should use this defensepoint (0=Red, 1=Blue, 2=Green, 3=Gold). The Priority attribute is used in team games to determine which DefensePoints will be manned first (higher priorities are manned earlier). The FortTag attribute is used in Assault games to associate a defensepoint with a particular FortStandard.

BlockedPaths are used to keep bots from attempting to follow routes which are initially blocked (such as a bridge which must be lowered). . When triggered, the BlockedPath is unblocked. Once unblocked, a BlockedPath cannot be re-blocked. If using a BlockedPath, make sure that there aren't any path connections which bypass the BlockedPath and render it useless. AS-Mazon has some good examples of the use of BlockedPaths.

TranslocDests are a special type of LiftCenter which are not associated with a mover. TranslocDests are used to define places that bots can use a translocator to cut a route short. A lifttexit should be placed at the spot where the bot should stand a use his translocator, and a TranslocDest should be placed where the Translocator Target should be thrown. Another liftexit should be placed near the TranslocDest, as an "exit" from this "lift". The LiftExits and the TranslocDest should be given the same LiftTag.

Bots will assume that any LiftExit with the same LiftTag as a TranslocDest can be directly reached from the TranslocDest, without any special action on the bot's part. If the bot must use the translocator to get from the TranslocDest to one of the LiftExits, replace that LiftExit with a TranslocStart.

JumpSpots are similar to TranslocDests, and are also placed in conjunction with LiftExits all given the same LiftTag. Bots understand that in low gravity areas, or in JumpMatch, or if they have JumpBoots, they can also jump to reach JumpSpots. In addition, if the bImpactJump attribute of the JumpSpot is true, higher skill bots will impact jump (using the impact hammer) to reach the JumpSpot. If the bAlwaysAccel attribute is true, bots will use air control to reach the JumpSpot (try this only if they are failing without it).

Teleporters send any pawn which touches them instantly to their destination teleporter, as specified by their URL attribute. If the URL attribute is empty, the teleporter acts only as a teleporter destination. If it contains the same string as the tag of another teleporter in the level, that teleporter will be the destination. If it contains a string in the form of LevelName/TeleporterName, it will teleport to a different level on this server (single player only). If it contains a string in the form of Unreal://Server.domain.com/LevelName/TeleporterName, it will teleport to a different server on the net.

If a teleporter's bEnabled attribute is false, then it will not teleport pawns which touch it, although it can still act as a teleporter destination. The bEnabled attribute is toggled by triggering the teleporter. If the bChangesYaw attribute is true, pawns arriving at this teleporter will have their yaw changed to the teleporter's yaw. If the bChangesVelocity attribute is true, pawns arriving at this teleporter will have their velocity set to the teleporter's TargetVelocity.

Tweaking and Debugging the Navigation Network

After placing and "defining" the navigation network, the level designed should tweak and debug it by watching bots play the level. The designer should the game as a spectator, and add first one and then later several bots to the level. The designer can watch them play, advancing through the bot cameras using the fire button, and looking for problem areas. If the bot currently being viewed seems to be acting strangely, using the console command verbose will indicate what it is thinking. After the play session, reviewing the log file may also provide insight to problems.

If the bots don't seem to understand how to get to a certain destination, the designer can use the rememberspot and showpath console commands to look for breaks in the navigation network. When the rememberspot command is used, the current location is saved. Subsequent showpath commands will cause the best visible NavigationPoint on the path to the remembered location to become visible. If no NavigationPoint becomes visible, there is no valid path to the remembered location.

If a player cannot fit in the position specified by a PlayerStart, then the player cannot properly enter the game and will either wander as a spectator or the game will crash. The log file for that play session will have information on which PlayerStart caused the failure.

UT Game Types

The Unreal Tournament team games require special actors to be placed in the level.

Domination

Domination requires ControlPoints, which are a type of NavigationPoint, to be placed at strategic locations in the level. As with all NavigationPoints, these must not be placed too close to another navigation point, and they must be placed at ground level or bots will be confused by them.

ControlPoints have several editable attributes of interest. When a player gains control of a ControlPoint (by touching it), the optional event related to the player's team is triggered. These events are specified by the RedEvent, BlueEvent, GoldEvent, and GreenEvent attributes. The bSelfDisplayed attribute specifies whether the ControlPoint mesh (which depends on which team controls it) is visible. The PointName attribute gives a human readable name to the ControlPoint. The ControlSound attribute specifies the sound to be played when control of the ControlPoint changes.

Capture the Flag

Capture the Flag requires FlagBases, which are a type of NavigationPoint to specify the home location of the flag for each CTF team. There can only be two CTF teams, and each can only have one FlagBase. The team attribute of the FlagBase specifies to which team the FlagBase is associated, with 0=Red, and 1=Blue.

AlternatePaths are used to indicate alternate routes that bots can use to enter an opponent's base. The AlternatePath's team attribute should be the same as the team of the FlagBase for which the AlternatePath is pointing out a route. The AlternatePath's SelectionWeight is used to modify the likelihood of bots using that path. Bots use AlternatePaths by first moving along the shortest route to the AlternatePath they have chosen, and then moving along the shortest route from their to the FlagBase. If the AlternatePath is set up so that the shortest route to the AltermatePath involves running right by the FlagBase, bots will do that (a bad thing!). The location of AlternatePaths may need to be adjusted to provide the desired results. AlternatePaths with the bReturnOnly flag set true are used by bots with the same team number returning to their base with a flag.

Assault

The FortStandard is used to define the objectives in the Assault map. There can be multiple FortStandards in a map. The bFinalFort attribute if true specifies that when the FortStandard is destroyed, the level is completed even if there are other FortStandards still active. The DefensePriority attribute specifies the order in which the FortStandards should be defended, with the highest DefensePriority FortStandards defended first. The NearestPathNodeTag attribute should always be used to provide bots a hint about how to get to the FortStandard. Set it to the tag of the nearest reachable PathNode. The optional FallBackFort attribute specifies which fort defenders should fallback to if the FortStandard is destroyed (set the FallBackFort to the tag of the desired FortStandard). The bTriggerOnly attribute specifies that the FortStandard should be triggered (using a trigger or by touching it) rather than shot.

The bSelfDisplayed attribute specifies that the FortStandard is visible (using its mesh). This is typically turned off and the FortStandard is displayed with level geometry. The bFlashing attribute specifies that the FortStandard should flash when it is damaged. The bSayDestroyed attribute specifies that bots should announce when they have destroyed this FortStandard (turn this off if the FortStandard marks a strategic location, and not a visible objective). If the DestroyedMessage is not empty, it will be displayed on everyone's HUD when the FortStandard is destroyed. The DamageEvent[] and DamageEventThreshold[] arrays are used to specify events which occur when the FortStandard has incurred specific amounts of damage.

The ChargeDist and bForceRadius attributes are advanced AI tweaks used to determine how aggressively bots will go after a FortStandard, when they feel they don't have adequate backup.

One of the FortStandards in the level should have the DefenseTime attribute (the total time the base needs to be defended) set. Also, one FortStandard should have the EndCamTag attribute (the tag of the spectatorcam to use when the game ends).

The TeamCannon can be placed in Assault levels to support defenders. The MinigunCannon is a minigun version of the TeamCannon.

Every Assault level must have an AssaultInfo somewhere in it. The AssaultInfo contains the text (in the ObjDesc[] array) and screenshots (in the ObjShots[] array) which describe the objectives for the level. The NumObjShots should be se to the number of text/screenshot elements.

The AssaultRandomizer can be used to force bot attackers to vary their entry routes randomly. Place an AssaultRandomizer in a spot along the shortest path, making sure there are no connections around it along that path. Set the ToggledCost attribute to an appropriate value to increase the distance cost of that path. The use a trigger to toggle the AssaultRandomizer on and off as desired, causing the path to appear longer or shorter. Never use a negative value for the ToggledCost.

The Level InfoPlayerCount string.

Your preview screenshot must be a 256 x 256 pixel, 256 color .PCX or .BMP file. In UnrealEd select Textures in the Browser window and then click the Import button to import your screenshot. A file dialog will pop up. Browse to the location where your preview is stored and select it, then click the OK button. The Import Texture dialog will pop up. In the "Name:" field enter "screenshot" and in the "Package:" field enter "MyLevel". Uncheck the "Generate MipMaps" option and then click the OK Button. After UnrealEd works for a moment you should see the "MyLevel" package displayed in the browser with your preview screenshot shown in the browser texture window. Go to Level Properties from the Ued's drop down menus and there's a field called "Screenshot" (or something like that); you need to click on your newly created textured screenshot in the Browser window to have the name come up] Rebuild and save your map. (Thanks Steve Keene for this explanation.)

When you run UT and select your map (when starting a practice session or network game) you should see your screenshot displayed just as it does with all of the maps that came with UT.

Trigger

The Trigger actor is used to trigger events. When a Trigger is activated, it broadcasts its event to all other actors in the level. Any actors whose tag matches this event will be triggered.

The TriggerType of a Trigger specifies what situations will cause a trigger to activate. The possible settings of TriggerType are

TT_PlayerProximity     Trigger is activated by player (or bot) proximity.

TT_PawnProximity       Trigger is activated by any pawn's proximity.

TT_ClassProximity      Trigger is activated by actor of the class specified

                       by the Trigger attribute ClassProximityType.

TT_AnyProximity        Trigger is activated by any actor in proximity.

TT_Shoot,              Trigger is activated by player shooting it.  The optional

                       DamageThreshold attribute specifies the minimum damage                        required for triggering (instantaneous, not cumulative).

The attribute ReTriggerDelay specifies the minimum time before a trigger can be triggered again. The bTriggerOnceOnly attribute, when true, specifies that the trigger should only activated once and then go dormant.

There are several possible states for the Trigger actor. The desired state is set using the InitialState attribute (in the Object section of the Trigger properties). The alternate states are used for Trigger actors that are turned on or off by other triggers, in conjunction with the bInitiallyActive property. If bInitiallyActive is false, the Trigger will be disabled initially. The possible states for the Trigger actor are:

NormalTrigger           Triggering the Trigger has no effect.

OtherTriggerToggles     When triggered, the Trigger will toggle its

                        bInitiallyActive property on and off.

OtherTriggerTurnsOn     When triggered, the Trigger will turn be come active.

OtherTriggerTurnsOff    When triggered, the Trigger will turn be come inactive.

RepeatTriggerTime, if set to a value greater than zero, causes the trigger to send out its event at an interval of RepeatTriggerTime seconds while the triggering actor remains within the trigger’s radius.

Special Trigger Types

The TimedTrigger is not triggered by being touched like other triggers. Rather, it triggers at a fixed interval after the level starts, specified by its DelaySeconds attribute. If its bRepeating attribute is true, the TimedTrigger will continue to trigger at every DelaySeconds interval.

The TeamTrigger is only activated by pawns which do not belong to the team specified by its Team attribute.

Triggers

Classes which are subclassed from Triggers, but are not subclassed from Trigger, are recipients rather than sources of trigger events.

The SpecialEvent has several possible states. The desired state is set using the InitialState attribute (in the Object section of the SpecialEvent properties).

DisplayMessage             Just displays the message in the Message attribute.

DamageInstigator           Damages the instigator who triggered this event.

                           The Damage attribute specifies the amount of damage.

                           The DamageType and DamageString attributes are also relevant

                           for this state.

KillInstigator             Kills the instigator, using the DamageString for his

                           death message if the DamageType is SpecialDamage.

PlaySoundEffect            Plays the sound specified by the Sound attribute at

                           the SpecialEvent's location.

PlayersPlaySoundEffect     All players play this sound effect at their location

                           on the client side (so they hear it at full volume).

PlayAmbientSoundEffect     Places the SpecialEvent's AmbientSound on the instigating player.

PlayerPath                 Sends the instigating actor on a spline path through the level

                           as specified by InterpolationPoints placed in the level.

                           If the Message string is not empty, it will be sent to the

                           instigating player, unless bBroadcast is true in which case

                           it will be broadcast to all players.  This will happen

                           in any SpecialEvent state.

TBD: TriggerLight, DistanceViewTrigger, Kicker, Dispatcher, Counter

ZoneInfos

This section describes the attributes of ZoneInfos which are relevant to gameplay. For more information on the importance of properly zoning your level for rendering performance, review Zones.htm. Every zone can optionally include a ZoneInfo which specifies special attributes of that zone. Zones with no ZoneInfo in them use the LevelInfo's zon attributes.

There are several pre-defined ZoneInfo classes which should be used when possible, including WaterZones, LavaZones, and SlimeZones. The relevant attributes for these ZoneInfo classes are already properly set. While any zone can be filled with water simply by setting its bWaterZone attribute to true, the zone will not have all the normal characteristics of WaterZones, such as entry and exit splashes.

Several ZoneInfo attributes affect physics within the zone. These include ZoneGravity (for which only the Z component is relevant), ZoneGroundFriction (used for walking), ZoneFluidFriction (used for swimming in water zones), ZoneVelocity (constant velocity added to moving actors in this zone), ZoneTerminalVelocity (which specifies the maximum falling velocity), and bWaterZone (which causes the zone to be filled with water if true). If a zone has a ZoneVelocity, the bMoveProjectiles attribute specifies whether this velocity should be imparted to projectiles and effects.

Zones with bWaterZone true can also specify an EntrySound, an ExitSound, an EntryActor (such as a splash effect), and an ExitActor.

Zones can be set to damage actors that enter them by setting the bPainZone attribute to true, and specifying a DamagePerSec value and a DamageType. Setting the bDestructive attribute to true will prevent fragments and gibs from hanging around in the zone, while settingthe bNoInventory attribute to true will prevent dropped inventory items from hanging around in the zone. The actual damage inflicted on a pawn depends on the DamagePerSec value and how much of the pawn is immersed in the zone. If the DamageType is SpecialDamage, then the DamageString will be used to describe deaths.

Zones with bKillZone set to true will instantly kill any pawn that enter them. Pawns will not take damage in zones with bNeutralZone set to true.

The ZonePlayerEvent is triggered whenever a player enters the zone.

The ZoneName (in the LocationStrings section of the ZoneInfo properties) is used to identify the location of teammates on the scoreboard or when giving orders to bots. In large zones, a level designer can place LocationID actors in the zone to provide a finer granularity of location descriptions. The CollisionRadius of the LocationID (in world units) determines the area in which the LocationID's LocationName will supercede the zone's ZoneName.

The ZoneTag is used to tag a zone which is to be triggered by a ZoneTrigger. WarpZones can be triggered to sequence through an array of destinations specified in their properties. Other zones will toggle the bPainZone attribute on and off when triggered.

The ViewFog and ViewFlash properties (in the ZoneLight section of the ZoneInfo properties) allow the level designer to change the player’s display color/fogging while the player is in that zone. ViewFog is used to color the screen. It is a vector whose components are RGB values between 0 and 1.

As ViewFog increases the brightness of the screen, ViewFlash should be used to keep it from over-brightening. ViewFlash is a vector whose components should be between 0 and –1 (for example (-0.5, -0.5, -0.5). It reduces the brightness of the screen. Typically, all the components will have the same value, which should have a magnitude about the same as the average magnitude of the ViewFog components to keep the screen from changing brightness.

Special Zone Types

When a player enters a TeleporterZone, he is teleported by the associated teleporter (specified by the TeleporterZone’s TeleporterTag).

TBD: PressureZone, VacuumZone, WarpZone

AI Difficulty Differences

Unreal Tournament has 8 AI skill levels, along with a continuous range of intermediate skill levels. Skill levels 0 to 3 are called "novice" skills, because bots at these skill levels move more slowly than players and their weapons do less damage than player weapons. Bots with skill levels between 4 and 7 move at the same speed as players and their weapons do the same amount of damage.

Lower skill bots turn more slowly, and take longer to acquire an opponent. They also have worse peripheral vision. Higher skill bots have better aim, and do a better job of leading opponents. Higher skill bots are also better able to strafe, or to move toward an objective while firing at an enemy at the same time. Low skill bots move much more slowly when strafing. Higher skill bots are less disoriented when hit, and tend to fire their weapons faster (although never faster than players can).

High skill (non-novice) bots also understand the special moves with weapons, including the shock rifle combo move, the sniper rifle headshot, and impact jumping with the impact hammer.

Console commands useful for tweaking levels

These commands can be typed into the console.

  • PlayersOnly Only player actors are ticked. Everything else in the level is frozen in place.
  • AddBots n Adds n bots to the level.
  • ViewClass xxx View the game from the point of view of a member of class "xxx". Successive executions of this function will iterate through the class. For example, ViewClass Bot will show you the game from the point of view of a Bot in the level, if there is one. If you are a spectator, you can viewclass any bot or player in the level. If you are on a team in a team game, you can only viewclass teammates.
  • CheatView xxx This is used to viewclass bots on the opposing team in a single player game.
  • Verbose Shows you what the bot you are currently viewclassing is thinking.
  • KillAll xxx Destroys all actors of class xxx (including subclasses). For example, KillAll Inventory will remove all inventory, including all weapons and pickups..
  • Fly Allows you to fly around the level.
  • Walk Returns you to the normal walking state.
  • Ghost Allows you to fly around the level and go through walls. While ghosting, you cannot pick up any items.
  • Loaded Gives you all the UT weapons except for the Redeemer.
  • AllAmmo Gives you 999 ammo for all the weapons you have.
  • Invisible b With b=1 makes you invisible, while b=0 makes you visible.
  • God Toggles god mode.
  • BehindView b Toggles your view from first person (b=0) to third person (b=1).
  • SloMo f Changes the rate of time in the game. 1.0 is normal speed. 2.0 is twice normal speed. and 0.2 is one fifth normal speed.
  • KillPawns Removes all pawns from the game.
  • Summon xxx Adds an actor of class ‘xxx’ right in front of you. This can be anything (a weapon, ammo, a creature, etc.). By default, the class is assumed to be in the botpack package. If it is in another package, you must prefix the package name, e.g. 'summon unrealshare.vase'.
  • RememberSpot Remember your current location (see ShowPath below).
  • ShowPath Show the recommended path back to location remembered by RememberSpot.

End


Unreal Audio Subsystem

Tim Sweeney
Epic MegaGames, Inc.
http://www.epicgames.com/

Audience: Level Designers, UnrealScript Programmers, C++ Programmers.
Last Updated: 07/21/99

Audio-related classes

UAudioSubsystem is the abstract base class of the sound subsystem.

The actual sound subsystem is implemented in a subclass of UAudioSubsystem. The reference implementation is UGalaxyAudioSubsystem, which supports the Galaxy music and sound system. However, UAudioSubsystem is general enough that it can be ported to other sound systems fairly easily.

USound contains a sound sample with the following characteristics:

  • .WAV format.
  • 8-bit or 16-bit format.
  • Monaural.
  • Either one-shot or looped.

UMusic contains a digital music file with the following characteristics:

  • 32 or fewer channels maximum (16 or fewer recommended).
  • .MOD, .STM, .S3M, .IT format.

UnrealScript interface

PlaySound (Actor class): plays a simple, non-looping sound effect emmanating from the actor. Use this for all normal game sound effects. You can specify the following parameters:

  • Sound: A sound effect; should be a one-shot (non-looping) sound.
  • Slot: One of the eight "slots" to play the sound in. This enables you to prevent more than one sound from playing on a particular slot. For example, when an enemy is shot several times in rapid succession, you want the successive sound effects to cut each other off, rather than hearing a chorous of simultaneous, overlapping screams.
  • Volume: Sound volume multiplier. 1.0 means "normal volume", 0.5 means "half volume", 2.0 means "double volume", etc.
  • bNoOverride: Whether this sound effect should override another sound playing on the same slot, or be ignored.
  • Radius: Radius of the sound in world units, defaults to 4096.0.
  • Pitch: Pitch multiplier. 1.0 means "normal pitch", 0.5 means "half pitch".

ClientHearSound (PlayerPawn class): called when a particular PlayerPawn actor hears a sound effect. This is how sounds are sent to clients in a network game. You may call this function directly (in a particular PlayerPawn) to have only that one player hear a sound.

ClientSetMusic (PlayerPawn class): Sets the music for a particular player.

UnrealEd music tips

In UnrealEd's music and sound browsers, you can listen to music and sound effects by double-clicking on them, or with the Play/Stop buttons. You can hear the level's ambient sounds while editing by clicking on the realtime button in the 3d view (it looks like a joystick icon).

Any actor in Unreal can emit an ambient sound. The properties to check out are AmbientSound, SoundRadius, SoundVolume, and SoundPitch. When you turn on UnrealEd's "Show Actor Radii" option in a 2d view, you can see the ambient sound radii as blue circles.

In Options/Level, you can set the default music, music section, and cd track for the level.

MusicEvent actors work similarly to SpecialEvent actors, in that they let you cause music changes in response to any event, such as the player touching a trigger, killing a monster, etc. In multiplayer games, MusicEvent actors only cause a music change in the player which caused the event, not all players in the game. MusicEvents have a "Song" variable which you can set to "None" if you want to use the song you set for the level in Options/Level.

3D positioning

A sound is positioned in 3d space, positioned at the Location of the actor who played the sound effect or ambient sound.

As the actor that played the sound moves around, the actor's sound is repositioned. The affects the following properties:

  • Distance attenuation.
  • Doppler shifting.
  • Stereo panning (if UseStereo is enabled).
  • Dolby Surround Sound positioning (if UseSurround is enabled).

Doppler shifting is based on the emitting actor's Velocity. Note that it is possible for actors to move around directly with SetLocation or MoveActor without having a valid Velocity, thereby causing no doppler shifting.

Dolby SurroundSound software encoding requires a Dolby decoder and four speakers, but it works on all stereo sound cards. It simply pans sound effects and ambient environment sounds between the four Dolby channels (center, rear, left, and right).

Sound slots

Each actor in the world has eight "sound slots", so it can play up to 8 simultaneous (overlapping) sound effects.

When an actor plays a sound in a slot where an old sound is already playing, the old sound can either be cut off and overridden, or the new sound can be ignored (see the bNoOverride parameter in PlaySound).

Sound slot zero (SLOT_None) is special: it indicates that a sound should not interrupt any other sounds. For sound effects like rapid-fire gunshots, use SLOT_None to allow multiple instances of the sound to play simultaneously.

In Unreal, there is a hard limit to the number of sound effects which may be playing simultaneously. This limit is set in the Unreal.ini file, and can vary from 16 to 32. During gameplay, the sound engine prioritizes each sound effect according to its volume and its distance from the listener, and culls the least important sounds. High-volume sounds override low-volume sounds, and sounds near the listener override sounds far away from the listener.

Sounds

Sound effects can be either 8-bit or 16-bit, and can be sampled at any reasonable rate (4kHz-44kHz). What rate and bit depth to use is entirely a quality vs. memory usage tradeoff. In general, use the lowest bit depth and sampling rate which gives you acceptable sound quality.

For best sound quality, make sure your sound effects are not clipped, i.e. sampled at so high a volume that the tops or bottoms of their waveforms are truncated.

Ambient sounds

Each actor in the level can have one ambient sound effect, which is specified in its AmbientSound property. This should refer to a sound effect which loops. If a non-looping sound effect is used as an AmbientSound, it will not play properly (it will only play once).

Persistence and correctness

Sounds have a radius. They diminish linearly according to distance and are culled when too far away to hear. Culling distance is a linear function of the sound's volume.

Ambient sounds persist across loads and saves.

Regular sound effects do not persist across loads and saves. A regular sound effect can only be heard by players (local and network) who can hear it when it begins playing. For example, if you have a 10-second sound effect and you start playing it in a room, then five seconds later a far-away player teleports into the room 5 seconds later, he will not hear the sound at all.

Looping sound effects

To create a looping sound effect in a .wav file, you need a sound editing program like SoundForge (http://www.sfoundry.com) which supports looping sound effects. In Sound Forge, follow these instructions:

  1. Run SoundForge.
  2. Load your .wav file (either 8-bit or 16-bit).
  3. Bring up the "Special/Edit Sample..." dialog.
  4. Click on the "Sustaining" choice (as opposed to "One shot").
  5. Select "File/Save As...".
  6. Make sure that the "Save Sampler Information in file" option is checked.
  7. Click on the "Save button".

Limitations

Software-mixed sound:

  • 3D sounds are only stereo panned, but not fully spatialized (i.e. phase shifted and bandpass filtered based on sound absorption in the ear as a function of position).
  • The sound system is limited to a maximum of 64 channels of music and sound. By default, 16 sound effects channels are allocated, leaving 48 for music. You can change this from the Unreal.ini file.
  • Each playing sound effect, ambient sound, and music channel uses CPU time. Therefore, we recommend keeping the number of sound effects audible to the player fairly low, and keeping music channel usage "lean and mean".

Hardware-mixed sound:

  • 3D sounds are spatialized by whatever the hardware supports. For example, Aureal pans it, phase shifts it, and applies a complex convolution and bandcap filter to model positional modulation of the sound by the head and ears.
  • The sound system is limited to X channels of hardware mixed sound (where X depends on the kind of hardware, and can be from 6 to 64 for existing sound hardware). Any sounds for which hardware acceleration is unavailable are emulated in software using the software mixer, and thus may sound different (less spacey) than the same sound played on hardware.

Dynamic music

Each level has a default song and CD track which is played throughout the level. You can specify the song in UnrealEd in "Options/Level" in the "Audio" tab.

Music can be transitioned dynamically via a SpecialEvent object hooked up to a trigger or any other event source.

Music is not zone based, because that would become annoying when backtracking through maps and in network play.

Music is tracked per player so it can be used equally well in single player and network play.

Zone-based echo and reverb

The audio subsystem includes a six-tap sound reverberation filter which enables you to customize the echo and reverb effects on a per-zone basis (there can be up to 64 zones in a level, so you can have quite a bit of variety in the audio postprocessing within a level). When you add a ZoneInfo actor into a zone, you can set the following properties:

  • bool bReverbZone: True to enable reverberation effects in the zone; False to disable them.
  • [this is outdated, but the quick version is: the other properties are 6 reverb tap delays and gains]

You can experiment with reverberation filters quickly by going to Options/Level, setting the reverb effects for the entire level, then double-clicking on sound effect names in the sound browser.

Music formats

Unreal supports two major kinds of music:

  • CD audio. You can compose your game's sound track, record it on CD, and specify in-level music by CD track. Advantages of CD audio include highest possible music quality, no CPU overhead, and giving composers the freedom to create music however they want. The major drawback is that you can't do dynamic music very well since switching between CD tracks isn't instantaneous, and you can't distribute it via the Internet.
  • Digitally mixed music (.mod, .s3m, .it, etc). These are special music formats which include digitally recorded instruments as well as sequencing data. Advantages: Easy to distribute on the Internet, and more amicable to dynamic music. Disadvantages: somewhat CPU intensive to mix (3%-5% of CPU time), lower sound quality than CD.

Windows platform implementation notes

We only support 16-bit sound output. We tested 8-bit sound output and the quality was unacceptable low: when you mix 64 channels of sound into an 8-bit output stream, you effectively get 2-bit sound effects. 2-bit sound effects are Not Good.

The sound system supports both the legacy WinMM sound support, and DirectSound. DirectSound generally achieves the lowest latency, while WinMM works on Win95 machines which don't have DirectSound, and WinNT.

The sound code makes heavy use of MMX to improve mixing quality and speed. You get about a 2X performance and quality boost on Pentium processors with MMX technology.

On non-MMX machines, the sound code makes some quality/speed tradeoffs by limiting sound effects to having only 64 volume levels. You can hear this limitation by setting up an ambient sound effect with a high radius in an otherwise quiet area: the discrete steps between volume levels are quite audible.


Composing Music for Unreal

Alexander Brandon
abrandon@uswest.net
Straylight Productions

UPDATE

Many apologies for the lack of updates to this page. They've been long overdue, and there is much to report regarding Unreal's music. Expect to see regular updates from now on. Also stop by the Straylight site for updates concerning interactive music using the Unreal Engine as well!

Last Updated: 07/09/1999

Programs to use

There are many choices now for composing music using the Unreal Engine. Unreal, Unreal Tournament, and the Unreal Mission Pack all used MOD files, which were chosen because of small file size and excellent quality compared to MIDI based files. New developments, however, have led to new ways of music reproduction in the engine.

  • TRACKERS: Tracker programs, used to write MOD files, can be found at "Maz' Sound Tools" page (listed below), along with information about how they are used and samples to use with them. The two trackers that are most widely used are "Impulse Tracker" and "Fast Tracker 2". Both have excellent features. Try them and see which one you prefer! Note that Tracker programs are usually either shareware or freeware. They are the least expensive way to get into making music for Unreal based games.
  • DIRECTMUSIC: DirectMusic Producer, included free with DirectX 6.0, is another easy way to get into music production. DirectMusic uses a MIDI based approach to composition, letting you create your own samples much the same way as you would use tracking programs. DirectMusic, however, has numerous ways of letting you manipulate your music and change it interactively with game events. For more information contact Chanel Summers.
  • REDBOOK / STREAMED AUDIO: CD audio and other forms of streamed audio are becoming much more popular now that they don't use as much in terms of processing time. However, they take up a LOT of space, and the more channels you use and the higher quality you use, the more space is needed. A 44khz 16 bit stereo piece 4 minutes in length takes about 45 megabytes of space, and if you wish to use more channels for surround sound (such as Dolby Digital AC-3), the size doubles or even triples in size from there. However, there are many forms of compression such as ADPCM that allow you to compress a 16 bit file to 4 or 8 bit with minimal signal degradation. Compression routines can be used in programs like Sonic Foundry's "Sound Forge" .. more info can be found at www.sonic-foundry.com. Unreal's "Galaxy" sound engine can play back many different audio file formats from Redbook CD audio to streamed WAV files.
  • MP3s: For commercial games, there is a tricky situation going on. The folks who supposedly created the MP3 compression program (Fraunhofer), have put a patent on it, which says that they get part of anything sold that uses this compression. Quite a pain, but there are proceedings being held all over the world against this patent, so it will either stand or be dropped, we'll have to wait and see. For non commercial use, however, such as freeware TC and MOD distribution, MP3s can be used just fine, and "Galaxy" will play them back in Unreal.

Frequently Asked Questions

How can I play Unreal music outside of Unreal?

Try going into UnrealED, go to the texture browser, select "Music", and load the song you want (.UMX file).. then press the Export button. You will be able to export the file selected as an Impulse Tracker (.IT) file. The .IT file can be then played in WinAMP or other Windows MOD players (available at Maz' Sound Tools).

I'm trying to use Impulse Tracker files with Unreal, but they won't play. Why?

The Galaxy audio engine within Unreal does not support certain extended features of Impulse Tracker, such as New Note Actions (when you use 'instruments' instead of 'samples' in IT, for instance). However, it DOES support ping pong looping, 16 bit files, up to 32 channels (64, if you don't want sound fx playing), and panning envelopes. No volume envelopes, though.

Can I play multiple WAV files for interactive music?

Yes. Galaxy can play up to 64 WAV files at once. Be warned however, that using more than 2 or three WAV files over 2 megs in size will slow down the system.

Did more people work on Unreal than you and Michiel Van Den Bos?

Yep! Andrew Sega contributed a song to Unreal. His nick is "necros" and he is currently working at Digital Anvil. Another contributor was Dan Gardopee, my partner and the co-founder of Straylight Productions.

Sites

  • "Maz's Sound Tools" is a great one-stop site for getting tracking programs, samples, info on soundcards, converters, and generally everything else under the sun. http://www.maz-sound.com/
  • DirectMusic Overview


Unreal Packages

Tim Sweeney
Epic MegaGames, Inc.
http://www.epicgames.com/

Audience: Level Designers, UnrealScript Programmers, C++ Programmers.
Last Updated: 07/21/99

About this document

This document has been written with our licensees in mind (Microprose, Ion Storm, Legend, and others). However, much of the info is useful to the Unreal community, so I am releasing it here unedited. The sections that refer to C++ code are not relevent to the community because we aren't releasing the C++ source. But, there is still a lot of useful information here.

Overview

An Unreal "package" is a collection of related objects such as textures, sound effects, and scripts which are grouped together for convenient distribution and use.

For example, in Unreal, textures are stored in texture package files, which use the ".utx" extension. A .utx file contains many (tens or hundreds) of textures which are part of a similar theme, such as the "SkyCity.utx" texture package.

Unreal's file format is based on packages because packages enable the game engine and content to be developed modularly. Artists maintain their textures in .utx files; the programmers working on the engine and game maintain separate packages of UnrealScript classes; people in the Unreal community can create and distribute their own packages online; and so on.

One package may refer to objects which are contained in another package. For example, an .unr level package may refer to textures which reside in various external .utx files; sounds in other .uax files; and music in a .umx file. We say that the level file is "dependent" on the other packages. To load that .unr level file successfully, you need to have all of the other .utx, .uax, and .umx files which that level refers to. If one of those packages is missing, you will not be able to load the .unr file.

Package files

All Unreal package files are stored in the same binary file format, but they use different extensions to identify their type:

  • .utx files are packages of textures.
  • .uax files are packages of audio (sound effects).
  • .umx files are packages of music.
  • .unr files contain one game level.
  • .u files are packages containing UnrealScript classes and objects (like textures, sounds, and meshes) they contain.

When the Unreal engine needs to load a package, it looks in the subdirectories listed in the [Core.System] section, in the Path#= list. The relevent lines of the clean (unmodified) Unreal.ini file look like this:

[Core.System]
CachePath=..\Cache
CacheExt=.uxx
Paths[0]=..\System\*.u
Paths[1]=..\Maps\*.unr
Paths[2]=..\Textures\*.utx
Paths[3]=..\Sounds\*.uax
Paths[4]=..\Music\*.umx

When you try to load a package by its raw name (such as "SkyCity"), the engine first searches in the CachePath for a matching package downloaded from the Internet; then it searches in each of the numbered directories in order (0, 1, 2...) for matching files. For example, it looks for \Unreal\Cache\[some funky name].uxx, then \Unreal\System\SkyCity.u, then \Unreal\Maps\SkyCity.unr, then \Unreal\Textures\SkyCity.utx -- and the file \Unreal\Textures\SkyCity.utx does exist, so it uses that file.

Because of this storage and search scheme, you must not create two packages with the same base name, which only differ by extension. For example, you must not create a texture file named SkyCity.utx and a music file named SkyCity.umx. If you do that, the engine will always fail to load one of the packages and give an error.

All Unreal package files (.utx, .uax, .umx, .unr, and .u) use the exact same file format, and are processed in exactly the same way by the engine. The only reason we define different extensions is for convenience, and to interoperate with Windows cleanly. For example, .unr files are known to contain levels, so we associate the Unreal icon with them, and set them up so right-clicking on an .unr file in Windows launches Unreal. And, UnrealEd knows that .utx files contain textures. The meanings of these file extensions are purely superficial. They all use the same file format.

Internet package file downloading and caching

Whenever you save a package file to disk, the file is internally stamped with a GUID ("Globally Unique Identifier"), which is a 128-bit number which is guaranteed to be unique. In single-player play, this GUID is never used and is irrelevent. However, when you connect to an Internet-based server, the server sends a list of packages that are required for gameplay there (for example, the level it uses, any texture packages it uses, any sound packages, etc). These packages are identified by name and GUID. If you don't have one of the packages required by the server, the package file is automatically downloaded to your \Unreal\Cache directory, and given a filename based on its GUID.

A typical \Unreal\Cache directory looks something like this:

Directory of F:\Unreal\Cache

06/02/98 03:37a <DIR> .
06/02/98 03:37a <DIR> ..
06/02/98 03:37a 8,932 2306087A11D1E2FD4F00DBA5CAA00349.uxx
06/02/98 03:38a 8,932 2401042A11D1E2FD4F00DBA5CAA00381.uxx
3 File(s) 8,932 bytes
698,601,472 bytes free

Packages stored in the \Unreal\Cache directory are identified by their GUID rather than the package name given to them by their creator, to avoid conflicting names. For example, there are bound to be a ton of levels on the net called Test.unr, a ton of textures named Walls.utx, etc. The GUID scheme avoids conflicts.

In UnrealEd, each time a package file is saved, it is assigned a new GUID. This way, each version of a package is guaranteed to be tracked and cached separate from other versions. This is good, because it prevents people from trying to play on a server using different versions of a level. This is bad, because if you simply load a texture file, resave it, and copy it to your UnrealServer, all of the players who come to your server will have to re-download the package. Therefore, we recommend modifying your package files only when necessary, and releasing them only after enough testing and use that you know they're not going to change and force redownloading a day later -- especially with large package files.

Why Unreal is based on packages

  • Packages enable an open plug-in architecture for easy community and licensee extensibility.
  • Packages make the Unreal engine expandable in content, UnrealScript code, and C++ code.
  • Packages help to improve the modularity of the code and reduce interdependencies.
  • Packages make it much easier for licensees to separarate the Unreal engine from the Unreal game.
  • Packages simplify Web-style downloading of content in Internet play.

Standard class packages that ship with Unreal

Core | The high-level framework Unreal is built on, similar to the MFC core or the Java Virtual Machine. The Core package contains the definitions of object, class, subsystem, script, property, name, and the object manager. The core is indepenent of any notion of a game or 3d engine.
Engine | The Unreal 3d engine, including support for vector math, actors, physics, collision, interaction, cameras, and networking.
Editor | The UnrealEd 3d editing environment with geometry editing, realtime lighting, and actor editing support.
Render | The Unreal rendering engine implementation.
Window | The platform-specific code supporting Unreal.
Galaxy | The implementation of Unreal's sound system (UAudioSubsystem) running on top of the Galaxy sound system for Windows. It is possible (for example, when porting Unreal to another platform) to completely replace the Galaxy package with an implementation of another sound system.
GlideDrv | Unreal 3d hardware support code for 3dfx's Glide library.
UnrealI | The Unreal I game implementation.
WinDrv | Windows viewport and client driver.
SoftDrv | Software rendering driver.
Fire | Fractal fire and water effects.
IpDrv | TCP/IP networking driver.

Licensees: Unreal package usage

Licensees may use all our .u packages except UnrealI verbatim or you may modify them to fit your needs. Bear in mind that it is attractive to use them in an unmodified form, as that makes merging with future code updates much easier and makes it easy to isolate problems in each others' code.

Licensees may use the UnrealI package as a reference and for internal development use, but the content in UnrealI is specific to our particular game, and it mustn't be used in your final product.

Licensees should create one more more new packages, for example "WheelOfTime" for their game-specific content. Keep in mind that your package and UnrealI can exist side-by-side during your development cycle until you've fully migrated away from the UnrealI specific content.

Licensee programmers: Sample directory layout for the "Engine" package

Here is an example of the directory structure for all the files that are related to the "Engine" package. The other packages follow a similar structure.

Distributable files:

\Unreal\System\Engine.dll | The package's C++ DLL file.
\Unreal\System\Engine.u | The package's UnrealScript class file.

Developer files:

\Unreal\Engine\Inc\Engine.h | Main public header file for the engine package.
\Unreal\Engine\Inc\*.* | Various public header files for the engine package, included by Engine.h.
\Unreal\Engine\Src\EnginePrivate.h | Private implementation header file.
\Unreal\Engine\Src\EngineClasses.h | Scripted classes exported to C++.
\Unreal\Engine\Src\Engine.cpp | Generates the package's precompiled header file.
\Unreal\Engine\Src\*.* | Various C++ files comprising the engine's code.
\Unreal\Engine\Lib\Engine.lib | The export library for linking with the engine.
\Unreal\Engine\Classes\*.uc | The UnrealScript classes used in building Engine.u.
\Unreal\Engine\Textures\*.* | Graphics files used in building Engine.u.
\Unreal\Engine\Models\*.* | Model files used in building Engine.u.
\Unreal\Engine\Sounds\*.* | Sound files used in building Engine.u.

Licensee programmers: Associating packages and DLL's

When you declare an UnrealScript class as "intrinsic", the engine looks in the .DLL file corresponding to the class's package for the class's corresponding C++ code. For example, here is the declaration of the "PlayerPawn" class in the "Engine" package:

//=============================================
// PlayerPawn.
//=============================================
class PlayerPawn expands Pawn
    intrinsic;

(You can find the above code in the file \Unreal\Engine\Classes\PlayerPawn.uc.)

When you declare a class as "intrinsic" in UnrealScript, you are telling the engine that you have created some corresponding C++ code for the class. In this way, you can mix and match UnrealScript and C++ code when you write your game code.

When you declare "intrinsic" classes, Unreal looks for the .DLL file in the \Unreal\System directory whose name matches your package's name. For example, to find the C++ implementation of the PlayerPawn class, Unreal looks in the "\Unreal\System\Engine.dll" file.

In C++, you must use the IMPLEMENT_INTRINSIC macro (defined in UnObjBas.h) to tell the engine that you have implemented a new class in C++. Internally, this macro generates some __declspec(DLL_EXPORT) declarations that Unreal looks for in your DLL. For example, the PlayerPawn class is exposed in the source file "\Unreal\Engine\UnPawn.cpp" as follows:

IMPLEMENT_CLASS(APlayerPawn);

In addition, once per package you need to use Unreal's package declaration macro to expose the package:

IMPLEMENT_Package(Engine);

So, to summarize:

  • Each Unreal package corresponds to a .u file, for example, "\Unreal\System\MyPackage.u".
  • If one or more classes in the package are "intrinsic" classes, then Unreal expects to find a C++ .DLL file corresponding to the package, for example, "\Unreal\System\MyPackage.dll".
  • Unreal dynamically loads and unloads packages and their optional, corresponding .DLL files, on the fly -- they are loaded and unloaded based on whether they are needed in the current game level.

Licensee programmers: Running the game and editor without the UnrealI.u package

You can start working with the "baseline" version of the Unreal engine (that is, the engine with all game-specific content removed), with:

    unreal.exe level.unr?class=engine.playerpawn?class=engine.gameinfo

While you're playing with the baseline configuration, you won't be able to see the player model and you won't be able to use any weapons because, of course, all of those things are game-specific and stored in the UnrealI package. But, the point is, you can edit and play levels with only the baseline engine, without any of our game-specific content.

See below for a description of what the Unreal.ini [EditPackages], [DefaultGame], and [DefaultPlayer] settings mean.

Licensee programmers: Replacing UnrealI.u with your own game-specific package

The first question is, "How do I create my own game package like your UnrealI.u?"

The easiest way to create a new package is by creating your first new class within UnrealEd. When creating a new game using the Unreal technology, you will need to create a custom "GameInfo" class to define high-level gameplay rules. Here's how:

  1. Run UnrealEd
  2. Bring up the "Classes" browser on the right-hand side of the screen.
  3. Expand the "Info" class.
  4. Right-click the "GameInfo" class.
  5. Pick the "Create new class below GameInfo..." menu option.
  6. Give the new class a name, for example "KlingonsGameInfo" and a package, for example "Klingons".
  7. Press F7 (or use the Script/Compile menu option) to recompile the new script.
  8. Save your package's .u binary file by hitting the "Save" button in the class browser and selecting your package.
  9. At the bottom of the class browser, hit the "Export" button to export all classes which you have modified. This generates *.uc files in a directory corresponding to your package. For example, if you just created a class named "KlingonsGameInfo" in a package called "Klingons", UnrealEd will generate the file "\Unreal\Klingons\KlingonGameInfo.uc". The .uc files are pure text versions of your scripts, which can later be cleanly rebuilt.

The next question is, "How do I have my new package used in the editor and during gameplay?" The Unreal.ini file defines many of these project-specific settings you will want to modify for your title:

  • The [EditPackages] section lists the class packages (.u files) to load for editing when UnrealEd is run. You will generally want to remove UnrealI from the list, and add your package(s) to the list, so that your level designers can see all of your content from within UnrealEd.
  • The [DefaultGame] section says which GameInfo-derived class is responsible for moderating gameplay. The GameInfo class (see \Unreal\Engine\GameInfo.uc) makes many high-level gameplay decisions in single-player and network play, i.e. it implements the game rules. For the baseline game, use Engine.GameInfo. To customize the game rules, you must create a subclass of GameInfo.
  • The [DefaultPlayer] section says which PlayerPawn-derived class should be spawned to represent the player in the game. For the baseline game, use Engine.PlayerPawn. To customize the player controls, mesh, etc., create a subclass of PlayerPawn.

Licensee programmers: Working with classes and packages in UnrealEd

The most important thing you need to realize while working with classes is that each class may be stored redundently in two separate places:

  • A binary copy of the class is stored in its package's .u file in the \Unreal\System directory. This binary copy of the class includes all objects referenced by the class. For example, the Skaarj class in the UnrealI package is stored in the file \Unreal\System\UnrealI.u. This binary version includes the Skaarj's mesh and all of the monster's sound effects. The binary copy of the class must be saved by using the "Save" button in the class browser. However, the binary file will break whenever the .u file format changes, which will happen several times prior to the release of Unreal.
  • For licensee programmers who have the entire Unreal C++ and UnrealScript codebase in standalone form, a text copy of the class is stored in one .uc file. For example, the text version of the Skaarj class is stored in \Unreal\UnrealI\Classes\Skaarj.uc. You can save these text files with the "Export" button in the class browser. The .uc file is a text file, so it doesn't contain any binary data. Rather, it contains UnrealScript "#exec" commands to import all ancillary objects like meshes, sound effects, and textures, when you perform a clean rebuild.

There are two ways you can create and edit UnrealScript classes:

  • You create new classes in UnrealEd by right-clicking on an existing class and subclassing it (by choosing the "Create new class below..." menu option), and then use the built-in script editor and compiler. To edit an existing class, just double-click on it. Use Script/Compile (or F7) to recompile your script.
  • You can create new classes from outside UnrealEd by creating a new .uc file in the package's directory. For example, to add a new class named "Timster" to the "UnrealI" package, create a new file called "\Unreal\UnrealI\Classes\Timster.uc". This way, you can use your favorite stand-alone editing tool, such as the text editor built into Visual C++, to edit your script. However, when you manually work with .uc files, you must cleanly rebuild your package in order to regenerate the binary .u file which UnrealEd requires.

To summarize:

  • You can create new classes in UnrealEd using the built-in editor, or you can work directly with the .uc files using your favorite stand-alone editor.
  • There is one text .uc for each class. Think of this as your source code.
  • There is one binary .u file for each package. Think of this as your object code.

Programmers: Cleanly rebuilding your packages from the command line

You can rebuild individual .u files, or all of the .u files, from the command line as follows:

  1. Delete the .u file(s) which you want to rebuild. For example, type "del \Unreal\System\*.u".
  2. Type "Unreal -make" to run Unreal and rebuild the .u files.
  3. Now, Unreal will go through all of the packages listed in the Unreal.ini [EditPackages] section, and rebuild any packages whose .u files are missing.

When Unreal rebuilds a package, it switches into the \Unreal\packagename\Classes directory (for example, if you're rebuilding the Engine package, it switches into \Unreal\Engine\Classes), and it imports all of the *.uc files it finds there.

The .uc files are text files containing UnrealScript code. There is one .uc file for each class. The .uc filename must correspond to the class name. For example, the PlayerPawn class must reside in the file "PlayerPawn.uc".

The .uc files may contain special UnrealEd commands which begin with the keyword "#exec". These "#exec" commands import various objects as the script is compiled. For example, #exec commands exist to import textures, sounds, meshes, and music. The "#exec" commands are not currently documented; you will need to see the example .uc files in the UnrealI package for examples of usage.

Rebuilding certain packages, such as the Engine package and the UnrealI package, requires that you have the source artwork, sounds, and meshes, which are available on the UnEdit ftp site as a separate download. The source data is not included in the regular source distribution because of its huge size. The source artwork can be extracted with WinZip into the various subdirectories of the Unreal\Engine\ and \Unreal\UnrealI\ directories (for example, the engine sounds go into \Unreal\Engine\Sounds).

When cleanly rebuilding, any UnrealScript compiler errors cause the rebuild to halt, and an appropriate error message is displayed on the screen.

To summarize:

  • Using "Unreal -make" cleanly rebuilds any missing .u files.
  • All .uc files (UnrealScript class definitions) are imported and recompiled.
  • The .uc files may contain "#exec" commands to import ancillary content.
  • The end result is that a new .u file is generated for each missing package.

Package file attributes: .upkg Files

When Unreal rebuilds your package using "Unreal -make", it tries to load a text PackageName.upkg definition file from the same \Unreal\PackageName\Classes directory. If not found, the default package options are used. A sample package definition file looks like this:

[Flags]
AllowDownload=False
ClientOptional=False
ServerSideOnly=True

The options that appear in a package definition file are:

  • AllowDownload: Whether you want players to be able to download this file when they enter a server. This should be enabled for user-created modifications, and disabled for game publisher distribution (so that people aren't able to download retail files freely).
  • ClientOptional: Whether the package is optional on the client side. Not currently supported; may be supported in the future for things like player skins.
  • ServerSideOnly: Says the package should only be loaded on the server side, and is not necessary for clients. For example, the IpDrv.u package is tagged as ServerSideOnly, because it contains classes like the server-uplink which only need to run on the server, and will be frequently updated.

Licensee programmers: Generating C++ header files which mirror your UnrealScript classes

If you create any intrinsic UnrealScript classes, or if you need to access any UnrealScript classes from C++ code, then you can have Unreal generate a C++ header file which mirrors the classes you've defined in UnrealScript.

For example, the Actor class any many of its child classes are defined by scripts in the Engine package (such as \Unreal\Engine\Classes\Actor.uc). The C++ code in the engine often needs to access objects in the Actor class. Early in development, we manually created C++ definitions which mirrored all of the UnrealScript classes. This process became tedious and error-prone as the number of classes increased. So, we added an option to Unreal to automatically generate a C++ header file, based on your UnrealScript definitions.

For an example of an automatically-generated C++ header file, check out "\Unreal\Engine\Inc\EngineClasses.h". This file was not written by a human. Unreal generated it.

To automatically generate a C++ header for a package, do the following:

  1. Delete the package's .u file. For example, type "del \Unreal\System\Engine.u".
  2. Run Unreal with the following parameters: "Unreal -make -h".
  3. Unreal will create a new file for you, called "\Unreal\Engine\Inc\EngineClasses.h".
  4. Now recompile the C++ source code.

Because there are many ways which UnrealScript and C++ can share information back and forth, the automatically-generated header file (for example EngineClasses.h) contains a variety of definitions:

  • The enumerations defined in the package.
  • C++ class definitions for all the intrinsic classes defined in the package. Note that non-intrinsic classes are not exported to the header file, because you can only access intrinsic classes from C++. (Note: this does not in any way limit what you can do in UnrealScript vs. C++).
  • For each class, C++ class member functions for all intrinsic functions defined in the class.
  • For each class, a C++ "#include" statement if a class-specific header file exists (for example, the Unreal generated C++ definition of the Actor class contains the #include "AActor.h" statement because the file \Unreal\Engine\Inc\AActor.h exists.

For a more concrete example, carefully examine the UnrealScript file "\Unreal\Engine\Classes\TcpLink.uc" and the following definitions in the file \Unreal\Engine\Inc\EngineClasses.h": enum ETcpLinkState, enum ETcpMode, and class ATcpLink. This provides an example of a fairly isolated UnrealScript class which defines several enumerations and intrinsic functions.

Finally, to avoid a chicken-and-egg scenario, you should create new classes in the following order: first create a class in UnrealScript, then generate the C++ header, then recompile the engine code (adding in the IMPLEMENT_CLASS macro for any new intrinsic classes you've added).

To summarize the important points here:

  • You created a new class in UnrealScript.
  • You needed to access that new class from C++.
  • You used the "Unreal -make -h" command to automatically generate a C++ header file defining the package's classes.

Programmers: Package related problems to avoid

  • Don't remove objects from packages which other people are relying on. For example, say level designers are using a texture package like SkyCity.utx which contains a texture named "SkyWall2" which they're using in many of their levels. If you go through and remove that texture from the .utx file, then the level designers won't be able to load their levels successfully. Rather they will get an error message along the lines of "Texture SkyWall2 was not found in package SkyCity.utx".
  • Avoid package filename collisions. Packages are identified by their base (extension-less) names, so you should node create a texture package named "Fred.utx" and a level named "Fred.unr". The base filename of each package must be unique.
  • Avoid circular package dependencies in .u files. For example, if you create two packages, "MyPackage" and "YourPackage" which both contain UnrealScript classes that refer to the other package, it will be impossible to cleanly rebuild both packages. During the clean rebuild process, packages are imported in the order they are specified in the Unreal.ini [EditPackages] section, and each package may only be dependent on the packages listed before it.
  • If you delete an UnrealScript, you must manually remove the .uc file corresponding to it. When you do a clean rebuild, Unreal imports all of the *.uc files in each package's directory.

Licensee programmers: Putting .uc script files under source control with SourceSafe

For programmers who like to keep their work archived under Microsoft Visual SourceSafe, here are some tips. We use SourceSafe internally for our development, though it is purely optional -- nothing in the Unreal source code assumes that you have SourceSafe:

  • Put all of your .uc files under source control (.uc files are text files containing the text of the UnrealScript code).
  • If someone removes a .uc file from source control, be sure to delete the corresponding .uc file on disk.
  • In the UnrealEd class browser, the "Export" button exports all scripts which you've modified. If you try to export a script which you haven't checked out of SourceSafe, you will get a (safe, non-fatal) warning message that the .uc file is write-protected. If that happens, just check the .uc file out of SourceSafe and hit the "Export" button again.

Programmers: Adding/Removing variables in UnrealScript classes can be dangerous

When you add or remove variables in an UnrealScript class, that changes the binary layout of objects belonging to that class. For example, if you have a script that contains some variable declarations like this:

class TestClass expands Actor;

var int i, j;
var texture t;

And you remove one of the variables like this:

class TestClass expands Actor;
var int i;
var texture t;

Then any objects in memory belonging to TestClass will become corrupted as soon as you recompile the script. The solution is to only add or remove variables from a script in an empty level. However, empty levels contain an actor belonging to the LevelInfo class, as well as one or more Camera actors, so it's never safe to add or remove variables from any of the following classes in UnrealEd: Actor, Info, LevelInfo, Camera, PlayerPawn, Pawn. To work with these files, edit their .uc files on-disk, and do a clean rebuild.

Also, bear in mind that Unreal makes heavy use of the Actor, Pawn, PlayerPawn, LevelInfo, and other intrinsic classes in C++ code. So, adding and removing variables to any intrinsic classes will cause the C++ code and UnrealScript code to disagree about the binary layout of classes. Whenever you change an intrinsic class, the safe thing to do is a clean rebuild with the "Unreal -make -h" option to generate new C++ header files for your classes, then recompile the Unreal C++ source.

Programmers: The package file format

Abandon all hope ye who try to parse this file format.

The Unreal package file format is only useful to people who want to write external utilities that read and write Unreal files. Level designers and UnrealScript programmers don't need to know this info. But, the Unreal package file format is quite complex compared to what Quake, Quake 2, and Jedi Knight utility writers are accustomed to. It's so complex, in fact, that I doubt many utility writers are going to want to deal with it directly. It is complex for several reasons:

  • Unreal files can contain a wide range of objects (there are hundreds of unique classes defined in Unreal), versus perhaps 20-30 for Quake.
  • Unreal objects can contain complex interdependencies: for example, a level package can refer to a texture package, which can refer to a class package, which can contain a class that refers to a sound in a sound package. So, each package file needs to keep track of not only the objects it contains, but also the external objects that it refers to.
  • Unreal objects are expandable by users, so we needed to create a generalized system for serializing (a.k.a. saving and loading) these complex user-defined objects.
  • Unreal objects are scoped within hierarchical packages (much like DOS and Windows files are scoped within a system of hierarchical directories). Therefore, the entire tree structure of the object scope hierarchy must be described within the file.

The coming release of the enhanced version of UnrealEd in 3+ months will include a very modular plug-in interface for creating built-in UnrealEd tools, file importing tools, and file exporting tools, which will operate directly on the engine's C++ data. That release will greatly simplify the task of programmers who want to write Unreal utilities, because it eliminates the need to read and write package files directly. Rather, with the UnrealEd plug-in interface, you will be able to use C++ to create all of your tools and compile them into DLL's. You'll make calls to the Unreal engine to allocate objects, save package files, load package files, etc. with the hairy file-format issues taken care of by the engine.

Data types stored in package files.

  • The "int" type is signed, 4 bytes long, and is stored in Intel byte order.
  • The "float" type is signed, IEEE standard, 4 bytes long, and stored in Intel byte order.
  • The "string" type is stored as a bunch of characters (one nonzero byte each) terminated by a zero byte.
  • The "FCompactIndex type" corresponds to a 32-bit signed value but are stored in a compacted format. See below for a description.
  • The "FGuid" type corresponds to a set of four consecutive int's defining a globally unique identifier.

Object and Name flags.

Each object and name stored in a package file can contain any combination of the following bitflags:

  • RF_LoadForClient (0x00010000): Must be loaded for game client.
  • RF_LoadForServer (0x00020000): Must be loaded for game server.
  • RF_LoadForEdit (0x00040000): Must be loaded for editor.
  • RF_Public (0x00000004): Object may be imported by other package files.
  • RF_Standalone (0x00080000): Keep object around (don't garbage collect) for editor even if unreferenced.
  • RF_Intrinsic (0x04000000): Class or name is defined in C++ code and must be bound at load-time.
  • RF_SourceModified (0x00000020): The external data source corresponding to this object has been modified.
  • RF_Transactional (0x00000001): Object must be tracked in the editor by the Undo/Redo tracking system.
  • RF_HasStack (0x02000000): This object has an execution stack allocated and is ready to execute UnrealScript code.

Any other bitflags stored in package files are irrelevent, should be set to zero when saving a package file, and should be ignored when loading a package file.

Compact Indices.

Compact indices exist so that small numbers can be stored efficiently. An index named "Index" is stored as a series of 1-5 consecutive bytes with the following C++ code. Basically, the "Ar << B0" type code serializes the byte stored in the variable B0. Serialize can mean read or write, depending on the internal implementation of the archive object Ar.

//
// FCompactIndex serializer.
//
FArchive& operator<<( FArchive& Ar, FCompactIndex& I )
{
           INT Original = I.Value;
    DWORD V = Abs(I.Value);
           BYTE B0 = ((I.Value>=0) ? 0 : 0x80) + ((V < 0x40) ? V : ((V & 0x3f)+0x40));
        I.Value = 0;
        Ar << B0;
        if( B0 & 0x40 )
        {
            V >>= 6;
            BYTE B1 = (V < 0x80) ? V : ((V & 0x7f)+0x80);
            Ar << B1;
            if( B1 & 0x80 )
            {
                V >>= 7;
                BYTE B2 = (V < 0x80) ? V : ((V & 0x7f)+0x80);
                Ar << B2;
                if( B2 & 0x80 )
                {
                    V >>= 7;
                    BYTE B3 = (V < 0x80) ? V : ((V & 0x7f)+0x80);
                    Ar << B3;
                    if( B3 & 0x80 )
                    {
                        V >>= 7;
                        BYTE B4 = V;
                        Ar << B4;
                        I.Value = B4;
                    }
                    I.Value = (I.Value << 7) + (B3 & 0x7f);
                }
                I.Value = (I.Value << 7) + (B2 & 0x7f);
            }
            I.Value = (I.Value << 7) + (B1 & 0x7f);
        }
        I.Value = (I.Value << 6) + (B0 & 0x3f);
        if( B0 & 0x80 )
            I.Value = -I.Value;
        if( Ar.IsSaving() && I.Value!=Original )
            appErrorf("Mismatch: %08X %08X",I.Value,Original);
    }
    return Ar;
}

Names

Names are stored a compact indices with values >= 0 which index into the file's name table.

Objects References.

Objects are stored as compact indices with values defined as follows:

  • If Index==0: The object is NULL (known as NULL in C++, None in UnrealScript).
  • If Index<0: Refers to the (-Index-1)th object in this file's import table.
  • If Index>0: Refers to the (Index-1)th object in this file's export table.

The Package file header.

  • int Tag: Always 0x9E2A83C1 (see PACKAGE_FILE_TAG in UnObjVer.h).
  • int FileVersion: Version of the engine which saved the file. Currently 61. Utilities should only operate on files with this exact version number, because only the file header is guaranteed not to change in future versions...anything else could change (See PACKAGE_FILE_VERSION in UnObjVer.h).
  • int PackageFlags: Bitflags describing the package:
    • PKG_AllowDownload (0x0001): The package is allowed to be downloaded to clients freely.
    • PKG_ClientOptional (0x0002): All objects in the package are optional (i.e. skins, textures) and it's up to the client whether he wants to download them or not. Not yet implemented; currently ignored.
    • PKG_ServerSideOnly (0x0004): This package is only needed on the server side, and the client shouldn't be informed of its presence. This is used with packages like IpDrv so that it can be updated frequently on the server side without requiring downloading stuff to the client.
  • int NameCount: Number of names stored in the name table. Always >= 0.
  • int NameOffset: Offset into the file of the name table, in bytes. 0 designates the first byte of the file, of course.
  • int ExportCount: Number of exported objects in the export table. Always >= 0.
  • int ExportOffset: Offset into the file of the export table.
  • int ImportCount: Number of imported objects in the import table. Always >= 0.
  • int ImportOffset: Offset into the file of the import table.
  • int HeritageCount: Number of GUID's stored in the package's heritage table. Always >= 1.
  • int HeritageOffset: Offset into the file of the heritage GUID table.

The following tables reside in the file at the offset specified by the header. If a table contains zero entries (as specified in the header), the offset value is meaningless and should be ignored.

Name table.

Contains a list of human-readable Unreal names (which correspond to the UnrealScript "name" datatype and the C++ "FName" data type). Each name is stored consecutively in the following format:

  • string NameString: String representation of the name, up to NAME_SIZE characters (currently 64, may increase with future versions).
  • int NameFlags: Internal flags describing the name. See "Object and Name Flags" above.

Export table.

Contains a list of objects contained in (a.k.a. "exported by") this file. Similar to a Windows DLL's export table.

  • object reference ClassIndex: Points to the class object describing the class of this object.
  • int PackageIndex: Points to the package object describing the package this object resides in.
  • object reference SuperIndex: If this is a field (a struct, class, property, or another field subclass): Points to the superfield object of the field.
  • FName ObjectName: This object's name.
  • int ObjectFlags: Flags.
  • int SerialSize: Size (in bytes) of the object's serialized data stored in this file.
  • if SerialSize>=0, int SerialOffset: Offset into this file of the start of the object's serialized data.

Import table.

Contains a list of objects in other packages which this packages refers to. Similar to a Windows DLL's import table.

  • name ClassPackage: The name of the package which this object's class object resides in.
  • name ClassName: The name of this object's class.
  • int PackageIndex: The index of the package this object resides in.
  • FName ObjectName: The name of this object.

Heritage GUID table.

Contains a list of GUID's which this package is compatible with. In the current version of Unreal, this table always contains exactly one GUID, because the backwards-compatible package management code has not yet been written. In the future, it may contain more than one GUID. Each entry contains:

  • A GUID (4 consecutive int's).

Serialize object format.

Extremely complex. Different for each class. Hard to read. Not yet documented.


Unreal Textures

Tim Sweeney
Epic MegaGames, Inc.
tim@epicgames.com
http://www.epicgames.com/

Audience: Artists, level designers.
Last Updated: 07/01/98

Texture dimensions

Unreal texture dimensions may be any powers of two from 1 to 1024. Textures need not be square. For example, many of the textures in Unreal 1 are 128x128, 256x128, and 256x256. Unreal's software rendering code supports textures of any size from 1x1 up to 1024x1024.

Unreal's 3D hardware rendering code gracefully handles textures of all sizes from 1x1 up to 1024x1024, though the actual rendered texture resolution is limited to texture sizes which the hardware supports. All current hardware is limited to 256x256 textures. For example, when you see a 1024x1024 texture on 3dfx hardware, Unreal substitutes a lower resolution (256x256) version of the texture. The texture is scaled up properly, so that the only noticeable artefact is blurring of the texture.

Because most current 3D hardware only supports textures up to 256x256, we generally don't use higher resolution textures in our game.

Creating and importing textures

Unreal textures are palettized, 256-color textures. Each texture in Unreal may have its own palette. The result is that you can achieve a lot of color variety by using textures with different palettes, while benefitting from the space savings of 8-bit textures.

The Unreal team creates textures in Fractal Painter and Photoshop, always working with the source art in 24-bit color. When a texture is complete, it is converted into a 256-color, palettized .pcx or .bmp file and imported into UnrealEd. To import a texture into UnrealEd, go into the texture browser (on the right side of the screen), and click on the "Import" button. When importing a texture, you can specify its name, the texture group it should appear in, and the package (.utx file) it should be saved in.

Unreal a texture mapping feature called "masking", where color zero is treated as if it were transparent. We use this for textures like vines, grating, and sprites. To create a masked texture, you will need to use a 256-color paint program to edit your image and apply color zero to the transparent portions of the texture. Then, import the texture and click on the "Masked" checkbox in the texture importing dialog. If you don't click on the "Masked" checkbox, the texture's mipmaps will not be properly masked.

When you import a texture, UnrealEd will automatically create mipmaps for the texture (mipmaps are lower resolution versions of the texture, used for antialiasing far-away polygons).

When you import two textures which use the same palette, UnrealEd makes note of this fact and takes advantage of it for optimization. In hardware rendering, the frame rate is partly dependent on the number of palettes visible in a scene, so there is a performance advantage to forcing textures to have the same palette. Programs like Image Alchemy can convert groups of 24-bit color images to 8-bit and force a common palette.

Graphics tools we use and recommend

  • Fractal Painter: For realistic painting of textures.
  • Photoshop: For realistic painting of textures and retouching of digitized textures.
  • Deluxe Paint: For creating masked textures, and for creating custom palettes.
  • Image Alchemy: For batch converting 24-bit color images to 256-color images.

Editing texture properties

To edit a texture's properties in UnrealEd, right-click on the texture in the texture browser (on the right side of the screen), and click on the "Properties" menu item. Here you can customize many properties of a texture. The properties which are not documented here are reserved for future use, and not yet supported.

Animating textures

The easiest way to create a sequence of animating textures is to import a bunch of textures with names that end in _A## (where ## is the number of the animation frame, starting at zero). For example, to create a four-texture animation sequence, import four files named:

  • MyAnim_A00.bmp
  • MyAnim_A01.bmp
  • MyAnim_A02.bmp
  • MyAnim_A03.bmp

When you import a texture into UnrealEd, it looks for _A## in the name and then sets up the texture for animation.

When you import animating textures into UnrealEd, the editor shows all of the individual frames of animation. However, you should always use the first one (the one with the name ending in A00) when setting the texture's properties and adjusting its frame rate. The following useful texture properties are related to animation:

  • AnimNext: The next texture in the animation sequence, or None to indicate that the sequence should proceed back to the first texture.
  • PrimeCount: For procedural animating textures, the texture internally animates itself PrimeCount times before it's first rendered. Use this to avoid algorithmic fire billowing up the first time it comes into view.
  • MinFrameRate: The minimum frame rate for the animation. The texture will always animate this fast, or faster (0 indicates that the texture should animate at the rendering frame rate).
  • MaxFrameRate: The maximum frame rate for the animation. The texture will always animate this fast, or slower. If you set this equal to MinFrameRate, the texture animation rate will never vary.

For texture animation, we support two animation rates (MinFrameRate and MaxFrameRate) to prevent "animation aliasing": the annoying artefacts you see when a texture animates through frames at a variable rate, such as "1 2 2 3 4 4 5". When the rendering frame rate is between MinFrameRate and MaxFrameRate, the texture will always animate smoothly, i.e. "1 2 3 4 5".

Detail textures and Macrotextures

A texture may optionally have a Detail texture and a Macrotexture associated with it. A detail texture is a very small, fine pattern which is faded in as you approach a surface, for example wood grain, or imperfections in stone. A Macrotexture is a large, scaled-up texture which is overlaid onto surface. For example, Macrotextures can be used to add large-scale detail to textures in outdoor spaces to avoid an overly "tiled" look.

Detail textures and Macrotextures only are visible on 3d hardware. For performance reasons, they are ignored in the software rendering code.

When you bring up the "texture properties" editor, you can associate a detail texture and a macrotexture with a given texture.

When you import a detail texture, you need to set its Scale value to a small number (typically 0.25) to specify its scaling relative to whatever texture it applies to. Macrotextures need to have their Scale value set to a large number (typically 8) to specify its scaling relative to the surface it's applied on.

It is valid to use procedural textures as macrotextures, microtextures, or both.

Detail textures and macrotextures modulate (multiply) the surface they're applied to. By modulating, they have the ability to scale the surface's brightness up or down. Colors with RGB brightness values from 0-127 darken the surface; 128 has no effect; and 129-255 brighten the surface. Therefore, when drawing microtextures and macrotextures, it is important that you design them with their brightnesses in the proper range so they affect surfaces naturally. If a detail texture's average brightness deviates too far from 128, then surfaces will appear to brighten or darken as you approach them.

Procedural textures

To create a new procedural texture, click on the "New" button in the texture browser and select a type of texture to create, for example "FireTexture" or "WaveTexture". Procedural textures are animated algorithmically so they have infinite, non-repeating animation. They also have the advantage of taking up very little disk space (on the order of 4K) and they only occupy as much memory as a single, non-animating texture of the same size. The procedural texture types and their options are documented elsewhere.

Texture .utx files

Unreal saves texture packages into .utx files. A .utx file contains all of the textures (one or more) in a particular package. The .utx file format contains the raw texture bitmaps as well as mipmaps, texture properties, and palettes. A game created with the Unreal engine will typically ship with many .utx files (one for each texture package).


Running UT Servers

Running Unreal Tournament Servers

Jack Porter
Epic Games, Inc.
http://www.epicgames.com/

Audience: UT Server Administrators
Last Updated: 02/26/00

Introduction

This document outlines the steps needed to configure an Unreal Tournament server from inside the game, and from the command line. It also explains how to use the WWW-based remote administration features built into UT.

Configuring and Launching a Dedicated Server from Inside the Game

Enabling Web-based Remote Administration

The UT server has an internal webserver which is used to remotely administer the game, but it is disabled by default. This is accessible under Options Menu, in Advanced Options. Expand the tree to find Networking | WebServer | bEnabled. Set bEnabled to True. You may also like to change the ListenPort value to something other than 80, if your server machine is already running another webserver such as IIS on port 80

Also in Advanced Options, under Networking | Web-based Remote Administration, you can find the username and password required to access the remote administration through your web browser.

Launching the Server

In the UT menus, choose Multiplayer | Start New Internet Game. Choose the gametype and map you want to run. Press the Map List button to configure the maps which will be cycled on your server. Use drag-and-drop to add or remove a map from the map list cycle. Press the Mutators button to enable any mutators you want to run on your server. Make sure Auto Change Levels is checked, or the server will run the same map over and over again. Go to the Rules and Settings tabs to ensure the game rules and settings are configured the way you want them.

If you want Bots on your server, go to the Bots tab and set the Minimum Total Players to a number greater than 0. If you choose 6, bots will come into the server when there are less than 6 human players connected. When a human connects, a bot will leave. In team games, be sure to check the Balance Teams checkbox so that the bots change teams to keep the number of players on each team balanced.

Under the Server tab, be sure to give your server a Server Name, and to check the Advertise Server checkbox. This advertises your server to the master server, so players can find your server even if they're not on your local LAN. Check the ngWorldStats Logging checkbox to have your server participate in ngWorldStats for UT. The Optimize for LAN checkbox should only be set if your server is intended for a LAN play only. DO NOT check this checkbox if you intend to have people on the Internet connect to you, or they might find internet play isn't as good as it could be.

Finally, press the Dedicated button to launch the dedicated server. Once your server is configured, you can later launch it from the command line - it will retain all the settings you've configured for it.

Launching a Dedicated Server from the Command Line

Enabling Web-based Remote Administration

Before you launch a dedicated server from the command line for the first time, you first need to edit your C:\UnrealTournament\System\UnrealTournament.ini file to enable WWW-based remote administration. The UT server has an internal webserver which is used to remotely administer the game, but it is disabled by default.

Open C:\UnrealTournament\System\UnrealTournament.ini with NotePad and find the following section:

[UWeb.WebServer]
Applications[0]=UTServerAdmin.UTServerAdmin
ApplicationPaths[0]=/ServerAdmin
bEnabled=False

Change the "bEnabled=False" line to read "bEnabled=True".

Should you need to change the port number the internal UT webserver runs on (eg if your server is already running another webserver such as IIS on port 80), add a new entry under [UWeb.WebServer] which says "ListenPort=8888", to run the UT webserver on the custom port number. Note that if you run multiple servers on the same machine (and bound to the same IP address), each webserver needs to be running on its own port number. More on this shortly.

Just below the [UWeb.WebServer] section is the [UTServerAdmin.UTServerAdmin] section. You need to change the username and password required to remotely admnister your UT server. If you leave your username and password set at the default values of admin and admin respectively, someone is sure to take control of your server and do evil things to your players.

Launching the Server from the Command Line

The syntax for running a server from a command line is as follows:

C:\UnrealTournament\System> ucc.exe server mapname.unr?game=GameType [port=portnum] [multihome=ipaddress] [ini=inifilename] [log=logfilename]

port - the optional base port number the server uses.
ini - the name of the ini file the server uses. This defaults to unrealtournament.ini
log - the name of the logfile the server generates. The default is ucc.log
multihome - the IP address the server should bind to, where the server has multiple local IP addresses.
GameType - one of Botpack.DeathMatchPlus, Botpack.Domination, Botpack.CTFGame.

Some examples:

ucc server dm-Turbine?game=Botpack.DeathMatchPlus ini=server1.ini log=server1.log
ucc server ctf-coret?game=Botpack.CTFGame multihome=204.12.54.28 ini=server2.ini log=server2.log
ucc server dom-Sesmar?game=Botpack.Domination multihome=204.12.54.29 ini=server3.ini log=server3.log

It's a good idea to make a small batch file which restarts the server should it crash:

:top
c:
cd \unrealtournament\system
ucc server dm-Turbine?game=Botpack.DeathMatchPlus ini=server1.ini log=server1.log
copy server1.log server1crash.log
goto top

Should you get a server crash, please mail the logfile to utbugs405@epicgames.com.

Multiple Servers Per Machine

Each copy of the UT dedicated server can serve one and only one level at a time.

However, you can run multiple level servers on one machine. Each copy of the server should have its own unique copy of the unrealtournament.ini file, with its own configuration. This is specified with the ini command line option. If you want to enable remote administration on multiple servers on the same machine, and you're not using multihome to specify a unique IP address to bind to, you'll need to give each webserver a unique ListenPort number.

You should give each server a unique base UDP port number. UT's default port number is 7777. To specify a port, use the port command-line option as shown above. Be sure to space the port numbers 10 apart as each server requires a number of UDP ports. eg:

ucc server ctf-coret?game=Botpack.CTFGame ini=server1.ini log=server1.log port=7770
ucc server dom-Sesmar?game=Botpack.Domination ini=server2.ini log=server2.log port=7780

Some Windows NT servers have more than one IP address defined (under Advanced in the TCP/IP control panel). If this is the case, you can run multiple servers on the same machine without configuring different port numbers. Specify the IP address for the server to listen on using the multihome option. This batch file starts two servers, each binding to a different IP address.

start ucc server ctf-coret?game=Botpack.CTFGame multihome=204.12.54.28 ini=server1.ini log=server1.log
start ucc server dom-Sesmar?game=Botpack.Domination multihome=204.12.54.29 ini=server2.ini log=server2.log

Remote Administration

Once the server is up and running, and the remote administration is enabled, you can access your Unreal Tournament server with any Web Browser. Internet Explorer 4 or 5 is recommended, but it should work fine with Netscape 4 also. To access your UT server from a web browser running on the server machine, Use a URL such as the following:

http://127.0.0.1/ServerAdmin

Or if your copy of UT's Webserver is running on a custom port number (with the ListenPort option)

http://127.0.0.1:8888/ServerAdmin

Of course, substitute 127.0.0.1 for the IP of your server to administer it from a web browser on another computer. Your web browser will then prompt you for a username and password. The remote admin is divided into two parts:

  • Current - Administer the current game in progress - view the player list, kick/ban players, add bots, change the current map, access the server console and chat to players.
  • Defaults - Change the map list, rules and settings for each game type, configure server-level options, and administer the IP ban list.

You'll want to change the following Defaults | Server options the first time you configure your server.

  • Server Name - the name of your server
  • Advertise Server - check this on to advertise your server to the master server
  • ngWorldStats Logging - tcheck this on to have your server participate in ngWorldStats for UT.

Performance

  • Make sure to match the maximum number of users for your server to the bandwidth you have available. The amount of bandwidth required for each user is determined by the MaxClientRate, which is set by default to 20000 bytes/sec. If your upstream bandwidth is limited (for example a cable modem), you should reduce this value. Make sure that the number of players your server allows (MaxPlayers) multiplied by MaxClientRate is less than your available bandwidth (in the case of cable modems or xDSL, your upstream bandwidth).

    The MaxClientRate variable can be changed by editing your UnrealTournament.ini file in your UnrealTournament\System directory. MaxClientRate is in the [IpDrv.TcpNetDriver] section.

    When you have your MaxPlayers or MaxClientRate set too high for your upstream bandwidth, you typically see everyone's ping get progressively higher, above 1000 and beyond. This occurs because your server is generating too much outgoing data for your internet connection to handle, and the data begins to back up.

> What should the MaxClientRate be set to, to allow good ping on an ADSL Server? And how many players would work well too?

It really depends on your ADSL provider. You need to work out what your maximum upstream bandwidth is in Bytes per second. One way to do this would be to ICQ a large file to a friend who is on DSL or cable, and look at what the transfer rate is, or if you know what it is in bits per second, divide by 8. ie 256kbps upstream bandwidth is around 32000 bytes per second.

Once you have that number, work out what maxclientrate you want to give everyone. 2600 is the bare minimum and it'll give modem-like netplay for everyone. 5000 is fine for modem and ISDN users but the play won't be quite as good as it could be for ADSL or cable users. 20000 is the largest you should ever go. The number of players you can support is the upstream bandwidth in bytes per second divided by the MaxClientRate you chose. So with 32000 bytes per second of available bandwidth, you could support:

32000 / 2600 = 12 players at modem quality play
32000 / 5000 = 6 players at ISDN quality play
32000 / 10000 = 3 players at better-than-ISDN quality play
32000 / 20000 = 1 player at LAN quality play.

I'd go for the 6 players at 5000 if all I had as 256kbps upstream bandwidth, because 5000 doesn't play too bad at all, and it's a good compromise.

  • We recommend disabling local NGStats for internet servers. Processing the NGStats logs takes some time between levels, which may result in remote clients getting disconnected. You can find this setting in the options->preferences menu, under the game tab.
  • Admininstrators can control downloading of packages from their server by editing the [IpDrv.TcpNetDriver] section of unrealtournament.ini:

    [IpDrv.TcpNetDriver]
    AllowDownloads=True
    MaxDownloadSize=0

    AllowDownloads disables all autodownloads.
    MaxDownloadSize=0 means allow autodownload of any sized file. Otherwise the value is in BYTES. We recommend that rather than disabling all downloads, you set the MaxDownload size to an appropriate value. For example, MaxDownloadSize=100000 will allow mutators and other small packages to be downloaded, but will not allow large files such as maps to be downloaded. Turning off all downloads may make it hard for clients to get on your server if you are using a lot of custom maps, mutators or other custom packages.

Links

  • The Admin Page - an Unreal Tournament community run site especially for server administators.
  • Server Admin Help at UTWorld - another community run server admin site.
  • Epic runs a utservers mailing list for server administrators to ask questions and discuss topics relating to running UT servers. This list gets a lot of traffic and therefore it is not recommended that you join unless you are a serious UT server administrator. If you still want to join, send an email to majordomo@epicgames.com and include the words "subscribe utservers" in the BODY of the message. I will approve your addition to the list.

- Jack


Unreal C++ Objects

Tim Sweeney
Epic MegaGames, Inc.
http://www.epicgames.com/

Audience: Licensee programmers who have access to the Unreal C++ source.
WORK IN PROGRESS -- This document is far from complete.
Last Updated: 07/21/99

Where do I look for info?

  • UnObjBas.h: FObjectManager (the global object manager, GObj) definition. UObject definition. Helper macros.
  • UnCorObj.h: Look here for definitions of some of Unreal's simpler object classes.

When do I need to write a C++ Serialize function?

C++ UObject-derived classes for which you write a script (a .uc file) automatically are able to serialize themselves completely. In this case, serialization simply involves the engine automatically going through all of the variables defined in the script (bytes, floats, object pointers, etc) and serializing them. Since the script defines the layout of variables, you don't need to write any serialization code because the engine already knows what to serialize.

If you don't have a .uc script for your class (meaning it's purely written and implemented in C++), you need to write a serialize function that overrides UObject::Serialize, to serialize the new variables you've added. Here' is a code fragment example.

class UWhatever : public UObject
{
    DECLARE_CLASS(UWhatever,UObject);

    // New variables.
    INT A;
    FLOAT B;
    FString C;

// UObject interface.
void Serialize( FArchive& Ar )
{
    UObject::Serialize( Ar ); // Must route to superclass to serialize the superclass variables and stuff.
    Ar << A << B << C; // Serialize these variables.
    if( Ar.IsLoading() )
    {
        // If you have any special cleanup code you want to execute when loading, stick it here (optional).
    }
}

How does saving a pointer to an object causes that object to be saved?

When you are saving a package (UPackage* Pkg) and during serialization to the archive (FArchive& Ar) you save a pointer to an object (UObject* Obj) by passing it to the archive (Ar << Obj), Unreal automatically saves two things: (1) the reference to the other object, so that it is automatically linked up with the proper object when you load it, and (2) the other object, *IF* it's in the package that is being saved. If the other object resides in another package, it only saves the reference to it as an "import".

Unreal serialization works like MFC serialization and Java serialization in that you save one base object (for example the ULevel) and the framework automatically serializes the entire graph of objects that are reachable to the base object. This is the standard object-oriented way of loading and saving stuff.

However, Unreal (unlike MFC and Java) has the concept of packages which enables saving only the objects in the graph which reside in a certain package. This approach combines the benefits of the traditional OOP serialization patter (being able to automatically save complex interrelated objects) with the benefits of Windows DLL style dynamic linking (being able to develop stuff as a bunch of modules, which publically export certain objects, and import other objects) which enables object-code modularization.

What resides in the Unreal binary package files?

An Unreal package file on disk corresponds to one top-level package (i.e. UnrealI.u corresponds to the UnrealI package, MyLevel.unr corresponds to a level named MyLevel).

From a high level point of view, an Unreal binary package file contains four distinct things:

  1. The name table (a table of C++ FName's) which maps unique text strings to indices.
  2. The import table, which lists all of the external objects, in other packages, that this package depends on. These are just described by name, just as the symbol table in a .DLL file lists functions imported from other DLL's.
  3. The export table, which lists all of the objects that reside in the package file, and tells where to find it within the current package file.
  4. The export data, which contains a bunch of raw serialized data for each exported object.

How do I write an external utility to read and write Unreal binary files?

The data in Unreal binary files describes highly complex interrelated objects whose characteristics are totally dependent on Unreal's object framework, so trying to read and write them outside of the engine is not a fruitful activity. When you need to get data in and out of the engine, the best way to do that is through plug-ins, either written in C++ (for editor tools, data importers, or performance critical plug-in code) or UnrealScript (for game stuff; UnrealScript doesn't execute in the editor).

The main categories of plug-ins of interest are:

  • Import tools for importing data from other file formats (graphics, models, etc) into the engine. For this, you'll want to write a new factory object (see the UFactory class, and UnEdFact.cpp for many examples). A factory is an object responsible for importing data from one or more files and constructing one or more UObject's from that data.
  • Export tools. Code for these can go into the game or editor. The typical approach is to traverse whatever Unreal objects you want and use fopen, fclose, fread, fprintf, etc. to write it out to a file.
  • New kinds of non-actor Unreal objects -- new algorithmic texture types, etc. The best approach for extending Unreal objects or creating new kinds of UObject derived classes is C++ subclassing. Here you take a base class like UObject, UTexture, etc. and you create a child class, and write whatever functions are appropriate for that class including, if your object is savable, a serialization function.
  • New kinds of actors -- the best approach is UnrealScript subclassing. You write a new .uc file. In this case, the engine takes care of all the details of serialization for you as long as you write a valid script.

Are package names hardcoded into files?

No! If you have a package named "MyPackage" and you save it to the file "NewName.u", the next time you load it, it will be called "NewName". Alternatively, when you load a package you can optionally specify its name (which may differ from the filename). Within a binary package file, there are absolutely no hardcoded references to the package's name.

How do "packages within packages" work?

Explain.

How can I best manage importing complex game data into new classes of C++ objects?

We've found that the easiest way to manage complex game objects (like your conversations) is to describe them in one or more UnrealScript classes residing in .uc files, add in a few new "#exec" commands in UnEdSrv.cpp to import your custom format data, and build those .uc files into a .u binary files using "unreal -make". For example, look at the humongous UnrealI.u file which contains a ton of scripts, sounds, textures, and meshes imported from a huge number of source files. With this approach:

1. You can create your basic content in text files (or other industry standard files) or your own simple, proprietary format.
2. You can change your UObject-derived object formats without completely breaking your content -- you just do another "unreal -make". This is important. I've gone through over 300 breaks of the .u file format!

What you will probably want to do is create one package containing ALL of your DConEvents, to avoid tying them to individual level.s One easy way to do this is:

1. Create a new directory (for example) \unreal\con, and \unreal\con\classes.
2. Create a script \unreal\con\AllConEvents.uc that looks something like this:

//=============================================================================
// AllConEvents.
//=============================================================================
class AllConEvents expands Object
    abstract;

// A variable to contain references to the sounds we're importing.
var object Refs[10];

// Import the sounds. Note: #exec commands are only executed when you rebuild from the
// command line using "unreal -make". See Packages.htm for more info.
#exec AUDIO IMPORT FILE="Sounds\Skaarj\amb1sk.WAV" NAME="amb1sk" GROUP="Skaarj"
#exec AUDIO IMPORT FILE="Sounds\Skaarj\syl07.WAV" NAME="syl07sk" GROUP="Skaarj"
#exec AUDIO IMPORT FILE="Sounds\Skaarj\syl09.WAV" NAME="syl09sk" GROUP="Skaarj"

// Force this class to reference the sounds we imported, so that they'll be saved
// in this package file.
defaultproperties
{
    Refs(0)=amb1sk
    Refs(1)=skl07
    Refs(2)=syl09
}

3. Add "Con" to the "EditPackages" section of Unreal.ini so it will be properly rebuild when you do an "unreal -make". (It searches thru all directories listed in the EditPackages section and rebuilds packages whose .u files have been deleted).

The above example assumes I want to stick a bunch of sound files in the package (stored as .wav's on disk, brought into Unreal as USound objects). If you want to import a bunch of conversations, you need to write a new "#exec" handler in UnEdSrv.cpp to import your data from your custom file format into some UObject derived class like DConversation.

How are objects unique identified and created within a package?

Within a package (which defines a namespace):

  • If you don't specify a name, Unreal creates a guaranteed unique name for you:
    • The guaranteed unique name is generated by appending a unique number onto the class name, for example Pawn23.
  • If you specify a name, and the name is unique, a new object is created.
  • If you specify a name, and the name matches that of another object of the SAME class, the other object is replace with the new one.
    • The old object's C++ destructor (and its UObject Destroy() function) are called.
    • The new object is constructed at the same memory location as the one it replaces.
    • Therefore any pointers to the replaced object are still valid, since they still point to the same class object.
    • The new object's C++ constructor is called.
  • If you specify a name, and the name matches that of another object of a DIFFERENT class.
    • You get a critical error message because this situation cannot be resolved safely.

End


IpServer Package

IpServer Package for Unreal v1.0

Brandon Reinhart
Epic MegaGames, Inc
brandon@epicgames.com
http://www.epicgames.com/

Audience:

  1. UnrealScript Programmers and Unreal Technology Licensees.
  2. Programmers developing third party information gathering tools.
  3. Server administrators interested in creating a master server uplink.

Last Updated: 09/14/98

References to Unreal's C++ code is only relevant to Unreal licensees who have access to Unreal source.

Overview

"I don't know what to do. I need a rendezvous."- Kraftwerk, Computer Love

Communication with other servers and systems is integral to the online gaming environment. Applications like GameSpy, QStat, and PingTool allow players a simple central application from which to manage the massive numbers of servers world wide. In addition, as games become more mature and bandwidth more available, the ability to link multiple servers in a network becomes more attractive.

The IpServer package for Unreal is provided for programmers interested in communicating with the Unreal game server, or programmers interested in expanding the information available through such communication. IpServer makes the underlying UDP architecture more or less transparent and allows an easy interface for communicating to external systems.

One of the goals in developing the IpServer query interface was to harness the power of Unreal's object oriented architecture. As such, it is possible to query for nearly any game, level, or player information Unreal or Unreal derivatives provide.

Another goal was to make integration with existing programs as easy as possible. IpServer is completely compliant with GameSpy's new query protocol. A copy of the GameSpy development docs can be obtained from Mark Surfas (mark@gamespy.com), but the majority of the protocol, as is relevant for communicating with Unreal, is documented here.

TCP & The Old Stuff

The IpDrv package has been broken up into IpDrv and IpServer. IpServer, documented here, contains all classes relevant for performing server uplinks and queries.

The old TCP versions of the Uplink and Query classes have been removed.

Master Server Uplinks with UdpServerUplink

UdpServerUplink and UdpServerQuery are the two classes in IpServer that handle master server uplinks and application queries, respectively. UdpServerUplink is instantiated when a server begins and if that server is configured for a master server uplink, it initializes the uplink and instantiates a UdpServerQuery class.

The first step in setting up the master server uplink is to configure the server. To do this, start Unreal, open the menu and go to Options::Advanced Options. A small config window will pop up. Inside the window, click on "Networking" then click on "Master Server Uplink Config" It will take a few seconds for the IpServer class to load. When the class is loaded, the menu will expand allowing you to set the following options:

DoUplink [true/false]
If DoUplink is true, then Unreal will attempt to connect to the master server. If false, it will not. Note that you must attempt to connect to a master server in order for the game server to answer queries. This can be changed by overriding the PostBeginPlay() function in UdpServerUplink.
Default: false
MasterServerAddress [IP Address/Domain Name]
This is the internet address of the master server. It can be specified as either a fully qualified domain name or as a dotted IP address. If empty, the default master server for the game server's region will be used.
Default: none
MasterServerPort [integer port num]
This is the port that the master server is listening for heartbeats on. For GameSpy, the master server listen port is 27900.
Default: 27900
QueryPort [integer port num]
This is the port that the game server will listen for queries on.
Default: 8777
UpdateMinutes [integer time in minutes]
This is the period of time, in minutes, that the game server will send a heartbeat packet to the master server. For GameSpy this should be 5 minutes.
Default: 5
UplinkPort [integer port num]
This is the port that the game server will bind for sending heartbeat packets to the master server. Specifiying 0 means that you do not have a port preference. A free port will then be assigned.
Default: 0
Region [int RegionCode]
This is a GameSpy code for the game server's geographical region. The default master server is also determined from this value.
Valid codes are:

0 - No Region Specified (any Region)
1 - Southeast US
2 - Western US
3 - Midwest US
4 - Northwest US, West Canada
5 - Northeast US, East Canada
6 - United Kingdom
7 - Continental Europe
8 - Central Asia, Middle East
9 - Southeast Asia, Pacific
10 - Africa
11 - Australia / NZ / Pacific
12 - Central, South America

Default: 0

Once you have configured the above options as you see fit, you are ready to start your game server. The UdpServerUplink package is automatically loaded whenever a new game server starts, so once the uplink is configured the work is done.

On the implementation side of things, UdpServerUplink is intentionally very simple. When PostBeginPlay() is called on the uplink object, it binds all appropriate ports, creates the query object, and sends out an initial heartbeat. It also sets up a timer to send heartbeat packets every UpdateMinutes * 60 seconds.

Query Service with UdpServerQuery

The core of the IpServer package is the UdpServerQuery class. When instantiated, a UdpServerQuery will listen for GameSpy Query Protocol formatted queries and respond with game information. If the game server is configured to perform a master server uplink then a query object will be spawned at the time the uplink is initialized.

Like UdpServerUplink, UdpServerQuery requires configuration prior to use. Go to the "Networking" menu as before and click on "Game Server Query" to open the configuration window. The following options are available:

AdminEmail [string 128]
This is a string identifying the email address of the game server administrator. If nothing is supplied, then nothing will be sent to the master server.
Default: none
AdminName [string 128]
This is a string identifying the organization or person running the game server. If nothing is supplied, then nothing will be sent to the master server.
Default: none
ServerName [string 128]
A name for the server. This can be anything.
Default: Another Unreal Server
ServerPort [int port num]
The port the server is running a game on.
Default: 7777

As the IpServer package develops, more user definable strings will come available. Feel free to email me with suggestions for new strings. I will not, however, be including the suggested AdminAge, AdminSex, and AdminPhoneNumber. :-)

Once the query responses are configured the setup is finished, just run a server to have the query server object answer queries.

Inside UdpServerQuery

Internally, UdpServerQuery is very simple. It binds all appropriate ports in the BeginPlay() function, which is called when the object is instantiated.

Once initialized, the UdpServerQuery is entirely event driven. It reponds to ReceivedText() events which are triggered whenever a query packet it received on the listen port.

The intercepted string is recursively parsed and answered in the ParseQuery function:

function string[240] ParseQuery(IpAddr Addr, string[240] Query, int QueryNum, out int PacketNum)

Query is the received string query. QueryNum and PacketNum are used for numbering response packets. Addr is the address of origin for the query.

ParseQuery is pretty straightforward. The intrinsic function ParseNextQuery takes a GameSpy querystring and parses out the first \key\optional_value pair. A success indicator is returned. QueryType contains the key, QueryValue contains the value, if any, QueryRest contains the remainder of the Query, and FinalPacket indicates whether the sectioned parsed is the last valid \key\optional_value pair in the Query. This is easier to understand if you know how the GameSpy Query Protocol works. (So I will briefly digress.)

--BEGIN DIGRESSION--

Each query is a series of \key\optional_value pairs. A simple query might look like:

\basic\

or

\echo\What time is it, foo?

A compound query might look like:

\basic\\info\\echo\What time is it, foo?\\players\

In the first case, ParseNextQuery() would return true. QueryType would contain "basic", QueryValue would be a null string, QueryRest would be a null string, and final packet would be true.

In the last case, ParseNextQuery() would return true. QueryType would contain "basic", QueryValue would be a null string, QueryRest would contain "\info\\echo\What time is it, foo?\\players\", and final packet would be false.

If valid strings are sent with abnormal leading or trailing characters, the abnormal characters will be ignored and discarded. For example, the query:

junkjunkjunk\basic\morestuffthat'sjunk

Would be parsed by ParseNextQuery() just like the query \basic\.

--END DIGRESSION--

If ParseNextQuery returns successfully, the path of execution switches on the QueryType. Here are possible valid queries:

\basic\
The basic query obtains the game name, game version, and server location:

\gamename\unreal
\gamever\[current version]
\location\[regional location]

The gamename is ALWAYS "unreal". This is so that querying programs can tell that the game server is, in fact, an Unreal server.

\info\
The info query obtains various important server information. Specifically, Unreal answers with:

\hostname\[server name]
\hostport\[server game port]
\hostip\[server game ip]
\mapname\[current map name]
\gametype\[current game or mod name]
\numplayers\[number of players]
\maxplayers\[max number of players]
\gamemode\openplaying

The gamemode is ALWAYS openplaying. This is to tell querying programs that the Unreal server's game can be dynamically joined. Obviously there aren't any carriage returns in the actual answer string.

\rules\
The rules query obtains rules for the current game. Specifically, Unreal answers with:

\timelimit\[time limit]
\fraglimit\[frag limit]
\MultiplayerBots\[true/false]
\ChangeLevels\[true/false]
\AdminName\[string name] *optional*
\AdminEMail\[string email] *optional*

Optional strings aren't sent if the answer would be null.

\players\
Players is used to obtain specific information on all connected players:

\name_N\[name of player N]
\score_N\[score of player N]
\team_N\[team of player N]
\skin_N\[skin of player N]
\mesh_N\[mesh of player N]

Ping and Time Playing will be added in the future. See below for more information on this response and its structure.

\status\
Status is a special query that obtains all the results of the basic, info, rules, and players queries combined.
\echo\echo_string
The echo query obtains a response that is identical:

\echo\echo_string

\secure\challenge_string
The secure query is used to verify that the server being queried is actually a legit Unreal server. The challenge_string is encrypted using a secret key and sent back:

\validate\response_string

\level_property\propname
A special query that is not in the GameSpy Query Protocol specification. This is discussed below, but the response is of the form:

\level_propname\propvalue

\game_property\propname
A special query that is not in the GameSpy Query Protocol specification. This is discussed below, but the response is of the form:

\game_propname\propvalue

\player_property\propname
A special query that is not in the GameSpy Query Protocol specification. This is discussed below, but the response is of the form:

\player_propname_N\propvalue

In addition, each packet is sent with an identifying number appended to the end:

\queryid\querynum_packetnum

Each query is assigned a unique identifier called the querynum. If the response to the query is broken up over multiple packets, each packet is numbered with a packetnum value. This is because the UDP protocol does not guarantee that packets will arrive in order. This system is detailed further in GameSpy's Query Protocol Documentation.
Example:

A client queries with \basic\ and the answer packet as \queryid\1_1 appended to the end. Another client queries with \status\ but the response is broken into multiple packets with the trailers \queryid\2_1 \queryid\2_2 \queryid\2_3 and so on.

Finally, the last packet in each response has the tag \final\ appended to it just before the \queryid\ tag. This is so that the querying application can determine how many packets are in the response.

UDP Issues and Packets

I have chosen to implement as much of IpServer in UnrealScript as possible. The idea is to make a server uplink/query system that is very extendable by third party developers and mod authors. As such, while the number of transferred packets is generally minimal, it may not always be optimal.

Here are some things to keep in mind about using IpServer:

  • UDP is an "unreliable" protocol. It doesn't check to see if packets have arrived at their destination and it doesn't resend packets that are lost.
  • UDP doesn't guarantee packet order. If you send data to another machine in seperate packets, those packets may not arrive in the order sent. Using a numbering system, like the \queryid\X_X system, to manage packet arrival.

The Property Queries

At this time, UdpServerQuery is capable of responding to three unique queries. Called the property queries, these queries allow the requesing server to obtain arbitrary member variables from certain game objects. The queries are:

\level_property\name of member variable
This query obtains a member variable from the server's current Level object.

\level_propname\propvalue

\game_property\name of member variable
This query obtains a member variable from the server's current Game object. The default behavior is to cast the Level.Game reference as a DeathMatchGame, so unmodified servers can only send members of DeathMatchGame.

\game_propname\propvalue

\player_property\name of member variable
This query obtains a member variable from each connected player's PlayerPawn.

\player_propname_N\propvalue of player N

Using the GetPropertyText method Unreal's object class provides, these queries can be used to obtain ANY non private information from the relevant objects. This can be of great use to query application authors who need information not provided by default responses but who don't want to subclass a new query server.

On the other end of the spectrum, UnrealScript programmers who are making their own mods can subclass the query server and, for example, cast the Level.Game reference as their own game type. Thus providing querying applications the ability to obtain special game information specific to the new mod.

Programmers could also take the provided Property Queries as examples to derive their own. For example, you could make an Actor property query that would use the AllActors iterator to obtain information on literally ANY actor instantiated on the game server. Obviously, significant design consideration will have to be put into how best to use such dynamic power (the potential for abuse is why a general Actor query is not included in the public release).

IpServer as a Model

IpServer serves as a good model on how to use the UdpLink package effectively. Read the code and come up with ideas for your own UdpLink derivatives. Possible ideas include:

- Linked Game Servers
(use GetPropertyText to send information on Actors to other servers)
- IRC/telnet Interfaces
(use the UdpLink or TcpLink capabilities to communicate with remote machines)
- Advanced Querying Systems
(use the query model as a guide to creating a versitile query interface for your Unreal engine game or modification)

Conclusion

IpServer will be regularly updated, but could forseeably achieve a plateau of functionality where more improvements are not necessary. The potential of the Property Queries implies that Unreal could work with future versions of querying tools with little work on either end (IpServer maintainer or query tool author). The Property Queries also set up the potential for very complex and very cool interserver relationships.

END


Unreal Networking Architecture

Tim Sweeney
Epic MegaGames, Inc.
http://www.epicgames.com/

Audience: Advanced UnrealScript Programmers, C++ Programmers.
Last Updated: 07/21/99

Note: References to Unreal's C++ code are only relevant to Unreal licensees who have access to the Unreal source.

History

Multiplayer gaming is about shared reality: that all of the players feel they are in the same world, seeing from differing viewpoints the same events transpiring within that world. As multiplayer gaming has evolved from the little 2-player modem games that characterized Doom, into the large, persistent, more free-form interactions of games like Quake 2, Unreal, and Ultima Online, the technologies behind the shared reality have evolved tremendously.

In the beginning, there were peer-to-peer games like Doom and Duke Nukem. In these games, each machine in the game was an equal. Each exactly synchronized its input and timing with the others, and each machine carried out the same exact game logic on exactly the same inputs. In conjunction with completely deterministic (i.e. fixed-rate, non-random) game logic, all players in the machine perceived the same reality. The advantage of this approach was simplicity. The disadvantages were:

  • Lack of persistence. All of the players had to start the game together, and new players couldn't come and go as they pleased.
  • Lack of player scalability: Because of the lock-step nature of the networking architecture, the coordination overhead and chance of network-induced failure increases linearly with the number of players.
  • Lack of frame rate scalability. All of the players had to run at the same internal frame rate, making it difficult to support a wide variety of machine speeds.

Next came the monolithic client-server architecture, pioneered by Quake, and later used by Ultima Online. Here, one machine was designated "server", and was responsible for making all of the gameplay decisions. The other machines were "clients", and they were regarded as dumb rendering terminals, which sent their keystrokes to the server, and received a list of objects to render. This advancement enabled large-scale Internet gaming, as game servers started springing up all over the Internet. The client-server architecture was later extended by QuakeWorld and Quake 2, which moved additional simulation and prediction logic to the client side, in order to increase visible detail while lowering bandwidth usage. Here, the client receives not only a list of objects to render, but also information about their trajectories, so the client can make rudimentary predictions about object motion. In addition, a lock-step prediction protocol was introduced in order to eliminate perceived latency in client movement. Still, there were some disadvantages to this approach:

  • Lack of open-endedness: When users and licensees create new types of objects (weapons, player controls, etc), glue logic must be authored to specify the simulation and prediction aspects of those new objects.
  • Difficulties of the prediction model: In this model, the network code and the game code are separate modules, yet each must both be fully aware of the implementation of the other, in order to keep the game state reasonably synchronized. A strong coupling between (ideally) separate modules is undesirable because it makes extensibility difficult.

Unreal introduces into multiplayer gaming a new approach termed the generalized client-server model. In this model, the server is still authoritative over the evolution of the game state. However, the client actually maintains an accurate subset of the game state locally, and can predict the game flow by executing the same game code as the server, on approximately the same data, thus minimizing the amount of data that must be exchanged between the two machines. Further, the "game state" is self-described by an extensible, object-oriented scripting language which fully decouples the game logic from the network code. The network code is generalized in such a way that it can coordinate any game which can be described by the language. This achieves a goal of object-orientation which increases extensibility, the concept that the behavior of an object should be fully described by that object, without introducing dependencies on other pieces of code which are hard-wired to know about the internal implementation of that object.

Basic Concepts

Goal

The goal here is to define Unreal's networking architecture in a fairly rigorous manner, because there is a fair amount of complexity involved that is easy to misinterpret if not defined exactly.

Basic Terminology

We define our basic terminology precisely:

  • A variable is an association between a fixed name and a modifiable value. Examples of variables include integers such as X=123, floating point numbers such as Y=3.14, strings such as Team="Rangers", and vectors such as V=(1.5,2.5,-0.5).
  • An object is a self-contained data structure consisting of a fixed set of variables.
  • An actor is an object capable of independently moving around in a level and interacting with other actors in that level.
  • A level is an object which contains a set of actors.
  • A tick is an operation that updates the entire game state given that a variable amount of time called DeltaTime has passed.
  • The game state of a level refers to the complete set of all actors that exist in that level and the current values of all of their variables at a time when a tick operation is not currently in progress.
  • A client is a running instance of Unreal.exe which maintains an approximate subset of the game state suitable for approximately simulating the events that occur in the world, and for rendering an approximate view of the world for a player.
  • A server is a running instance of Unreal.exe which is responsible for ticking a single level and communicating the game state authoritatively to all of the clients.

The Update Loop

All of the above concepts are well-understood with the possible exception of the tick and game state. So, these will be described in more detail. First of all, here is a simple description of Unreal's update loop:

  1. If I am a server, communicate the current game state to all of my clients.
  2. If I am a client, send my requested movement to the server, receive new game state information from the server, render my current approximate view of the world to the screen.
  3. Perform a tick operation to update the game state, given that a variable amount of time DeltaTime has passed since the previous tick.

A tick operation involves updating all of the actors in the level, carrying out their physics, informing them of interesting game events that have occurred, and executing any necessary script code. Unlike many past games such as Doom and Quake, all of the physics and update code in Unreal is designed to handle a variable amount of time passing. For example, Doom's movement physics looks like "Position += PositionIncrment" while Unreal's looks like "Position += Velocity * DeltaTime". This enables greater frame rate scalability.

While a tick operation is in progress, the game state is being continually modified by the code which executes. The game state can change in exactly three ways:

  • A variable in an actor can be modified.
  • An actor can be created.
  • An actor can be destroyed.

The Server Is The Man

From the above, the server's game state is completely and concisely defined by the set of all variables of all actors within a level. Because the server is authoritative about the gameplay flow, the server's game state can always be regarded as the one true game state. The version of the game state on client machines should always be regarded as an approximation subject to many different kinds of deviations from the server's game state. Actors that exist on the client machine should be considered proxies because they are a temporary, approximate representation of an object rather than the object itself.

Bandwidth Limitation

If network bandwidth were unlimited, the network code would be very simple: at the end of each tick, the server could just send each client the complete and exact game state so that the client always renders the exact view of the game as is occurring on the server. However, the Internet reality is that 28.8K modems have only perhaps 1% of the bandwidth necessary to communicate complete and exact updates. While consumers' Internet connections will become faster in the future, bandwidth is growing at a rate far lower than Moore's law which defines the rate of improvement in games and graphics. Therefore, there is not now and never will be sufficient bandwidth for complete game-state updates.

So, the main goal of the network code is to enable the server to communicate a reasonable approximation of the game state to the clients so that the clients can render an interactive view of the world which is as close to shared reality as is reasonable given bandwidth limitations.

Replication

Unreal views the general problem of "coordinating a reasonable approximation of a shared reality between the server and clients" as a problem of "replication". That is, a problem of determining a set of data and commands that flow between the client and server in order to achieve that approximate shared reality.

The Relevant Set of Actors

Definition

An Unreal level can be huge, and at any time a player can only see a small fraction of the actors in that level. Most of the other actors in the level aren't visible, aren't audible, and have no significant effect on the player. The set of actors that a server deems are visible to or capable of affecting a client are deemed the relevant set of actors for that client. A significant bandwidth optimization in Unreal's network code is that the server only tells clients about actors in that client's relevant set.

Unreal applies the following rules in determining the relevant set of actors for a player.

  1. The actor belongs to the ZoneInfo class, then it is relevant.
  2. If the actor has static=true or bNoDelete=true, then it is relevant.
  3. If the actor is owned by the player (Owner==Player), then it is relevant.
  4. If the actor is a Weapon and is owned by a visible actor, then it is relevant.
  5. If the actor is hidden (bHidden=true) and it doesn't collide (bBlockPlayers=false) and it doesn't have an ambient sound (AmbientSound==None) then the actor is not relevant.
  6. If the actor is visible according to a line-of-sight check between the actor's Location and the player's Location, then it is relevant.
  7. If the actor was visible less than 2 to 10 seconds ago (the exact number varies because of some performance optimizations), then it is relevant.

These rules are designed to give a good approximation of the set of actors which really can affect a player. Of course, it is imperfect: the line-of-sight check can sometimes give a false negative with large actors (though we use some heuristics to help it out), it doesn't account for sound occlusion of ambient sounds, and so on. However, the approximation is such that its error is overwhelmed by the error inherent in a network environment with such latency and packet loss characteristics as the Internet.

Changing the definition of the relevant set in C++

The set of relevant actors for a client is determined by the C++ function ULevel::GetRelevantActors. This way, licensees who want to implement different gameplay rules are free to modify our definition of the relevant set by modifying ULevel::GetRelevantActors or (better yet) subclassing ULevel, and overriding the function.

Actor Prioritization

In deathmatch games on modem-based Internet connections, there is almost never enough bandwidth available for the server to tell each client everything it desires to know about the game state, Unreal uses a load-balancing technique that prioritizes all actors, and gives each one a "fair share" of the bandwidth based on "how important" it is to gameplay.

Each actor has a floating point variable called NetPriority. The higher the number, the more bandwidth that actor receives relative to others. An actor with a priority of 2.0 will be updated exactly twice as frequently as an actor with priority 1.0. The only thing that matters with priorities is their ratio; so obviously you can't improve Unreal's network performance by increasing all of the priorities. Some of the values of NetPriority we have assigned in our performance-tuning are:

  • Bots: 8.0
  • Movers: 7.0
  • Projectiles: 6.0
  • Pawns: 4.0
  • Decorative creatures (such as fish): 2.0
  • Decorations: 0.5

Overview of Replication

Three Types of Replication

The network code is based on three primitive, low-level replication operations for communicating information about the game state between the the server and clients:

  • Actor replication. The server identifies the set of "relevant" actors for each client (actors which are either visible to the client or are likely to somehow affect the client's view or movement instantaneously), and tells the client to create and maintain a "replicated" copy of that actor. While the server always has the authoritative version of that actor, at any time many clients might have approximate, replicated versions of that actor.
  • Variable replication. Actor variables that describe aspects of the game state which are important to clients can be "replicated". That is, whenever the value of the variable changes on the server side, the server sends the client the updated value.
  • Function call replication. A function that is called on the server in a network game can be routed to the remote client rather than executed locally. Alternatively, a function called on the client side may be routed to the server, rather than called locally.

To give a concrete example, consider the case where you are a client in a network game. You see two opponents running toward you, shooting at you, and you hear their shots. Since all of the game state is being maintained on the server rather than on your machine, why can you see and hear those things happening?

  • You can see the opponents because the server has recognized that the opponents are "relevant" to you (i.e. they are visible) and the server is currently replicating those actors to you. Thus, you (the client) have a local copy of those two player actors who are running after you.
  • You can see that the opponents are running toward you because the server is replicating their Location variable to you. You can see them animating because the server is replicating their animation variables to you. In other words, the server is continually feeding you new values of their Location and animation parameters, at the rate of several times per second.
  • You can hear their gunshots because the server is replicating the ClientHearSound function to you. The ClientHearSound function is called for a PlayerPawn whenever that PlayerPawn hears a sound.

So, by this point, the low-level mechanisms by which Unreal multiplayer games operate should be clear. The server is updating the game state and making all of the big game decisions. The server is replicating some actors to clients. The server is replicating some variables to clients. And, the server is replicating some function calls to clients.

It should also be clear that not all actors need to be replicated. For example, if an actor is halfway across the level and way out of your sight, you don't need to waste bandwidth sending updates about it. Also, all variables don't need to be updated. For example, the variables that the server uses to make AI decisions don't need to be sent to clients; the clients only need to know about their display variables, animation variables, and physics variables. Also, most functions executed on the server shouldn't be replicated. Only the function calls that result in the client seeing or hearing something need to be replicated. So, in all, the server contains a huge amount of data, and only a small fraction of it matters to the client--the ones which affect things the player sees, hears, or feels.

Thus, the logical question is, "How does the Unreal engine know what actors, variables, and function calls need to replicated?"

The answer is, the programmer who writes a script for an actor is responsible for determining what variables and functions, in that script, need to be replicated. And, he is responsible for writing a little piece of code called a "replication statement", in that script, to tell the Unreal engine what needs to be replicated under what conditions. For a real-world example, consider some of the things defined in the Actor class.

  • The Location variable (a vector) contains the actor's location. The server is responsible for maintaining the location, so the server needs to send that to clients. So the replication condition basically says "Replicate this if I am the server".
  • The Mesh variable (an object reference) references the mesh that should be rendered for the actor. The server needs to send that to clients, but it only needs to be sent if the actor is being rendered as a mesh, i.e. if the actor's DrawType is DT_Mesh. So the replication condition basically says "Replicate this if I am the server and the DrawType is DT_Mesh".
  • In the PlayerPawn class, there are a bunch of boolean variables that define keypresses and button presses, such as bFire and bJump. These are generated on the client side (where the input happens), and the server needs to know about them. So the replication condition basically says "Replicate this if I am a client".
  • In the PlayerPawn class, there is a ClientHearSound function that tells the player that she hears a sound. It's called on the server but, of course the sound needs be heard by the actual person playing the game, who is on the client side. So the replication condition for this function might be "Replicate this if I am the server".

From the above examples, several things should be apparent. First of all, every variable and function that might be replicated needs to have a "replication condition" attached to it, that is, an expression which evaluates to True of False, depending on whether the thing needs to be replicated. Second, these replication conditions need to be two-way: the server needs to be able to replicate variables and functions to the client, and the client needs to be able to replicate them to the server. Third, these "replication conditions" can be complex, such as "Replicate this if I'm the server and my DrawType is DT_Mesh".

Therefore, we need a general-purpose way of expressing (complex) conditions under which variables and functions should be replicated. What is the best way to express these conditions? We looked at all of the options, and concluded that UnrealScript--which is already a very powerful language for authoring classes, variables, and code--would be a perfect tool for writing replication conditions.

The UnrealScript Replication Statement

In UnrealScript, every class can have one replication statement. The replication statement contains one or more replication definitions. Each replication definition consist of a replication condition (a statement that evaluates to True or False), and a list of one or more functions and variables to which the condition applies.

The replication statement in a class may only refer to variables defined in that class, and functions defined first in that class (that is, it cannot apply to functions defined in a superclass but overridden in that class). This way, if the Actor class contains a variable DrawType, then you know where to look for its replication condition: it can only reside there in the Actor class.

It's valid for a class to not contain a replication statement; this simply means that the class doesn't define any new variables or functions that need to be replicated. In fact, most classes do not need replication statements because most of the "interesting" variables that affect display are defined in the Actor class, and are only modified by subclasses. In Unreal, we have about 500 classes, and only about 10 of them need replication statements.

If you define a new variable or function in a class, but you don't list it in a replication definition, that means that your variable or function is absolutely never replicated. This is the norm; most variables and functions don't need to be replicated.

Here is an example of the UnrealScript syntax for the replication statement. This is taken from the Pawn class:

replication
{
    // Variables the server should send to the client.
    reliable if( Role==ROLE_Authority )
        Weapon;
    reliable if( Role==ROLE_Authority && bNetOwner )
         PlayerName, Team, TeamName, bIsPlayer, CarriedDecoration, SelectedItem,
         GroundSpeed, WaterSpeed, AirSpeed, AccelRate, JumpZ, MaxStepHeight,
         bBehindView;
    unreliable if( Role==ROLE_Authority && bNetOwner && bIsPlayer && bNetInitial )
        ViewRotation;
    unreliable if( Role==ROLE_Authority && bNetOwner )
        Health, MoveTarget, Score;

    // Functions the server calls on the client side.
    reliable if( Role==ROLE_Authority && RemoteRole==ROLE_AutonomousProxy )
        ClientDying, ClientReStart, ClientGameEnded, ClientSetRotation;
    unreliable if( Role==ROLE_Authority )
        ClientHearSound, ClientMessage;
    reliable if( Role<ROLE_Authority )
        NextItem, SwitchToBestWeapon, bExtra0, bExtra1, bExtra2, bExtra3;

    // Input sent from the client to the server.
    unreliable if( Role<ROLE_AutonomousProxy )
        bZoom, bRun, bLook, bDuck, bSnapLevel, bStrafe;
    unreliable always if( Role<=ROLE_AutonomousProxy )
        bFire, bAltFire;
}

The key things you see here are:

  • The replication statement is enclosed by "replication {}".
  • Each replication definition begins with "reliable if (condition)" or "unreliable if (condition)".

Reliable vs. Unreliable

Functions replicated with the "unreliable" keyword are not guaranteed to reach the other party and, if they do reach the other party, they may be received out-of-order. The only things which can prevent an unreliable function from being received are network packet-loss, and bandwidth saturation. So, you need to understand the odds, which we will grossly approximate here. The results vary wildly among different types of network, so we can make no guarantees:

  • In a LAN game, we guesstimate that unreliable data is received successfully approximately 99% of the time. However, in the course of a game, hundreds of thousands of things are replicated, so you can be sure that some unreliable data will be lost. Therefore, even if you're aiming for LAN performance only, your code needs to handle your unreliably replicated variables gracefully in the case that they are lost over the wire.
  • In a typical low quality 28.8K ISP connection, unreliable data is generally received 90%-95% of the time. In other words, it is very frequently lost.

To get a better feeling for the tradeoffs between reliable and unreliable functions, check out the replication statements in the Unreal scripts, and gauge their importance vs. the reliability decision we made.

Variables are always reliable

The "reliable" and "unreliable" keywords are ignored for variables. Variables are always guaranteed to reach the other party eventually, even under packet-loss and bandwidth saturation conditions. Changes in such variables are not guaranteed to reach the other party in the same order in which they were sent.

Summary

Here, we have documented the syntax of the replication statement thoroughly, without saying much about the meaning of the expressions like "Role==ROLE_Authority" and "bNetOwner". This is covered in the next section.

Replication Conditions

Here is a simple example of a replication condition within a class's script:

replication
{
    reliable if( Role==ROLE_Authority )
        Weapon;
}

This replication condition, translated into English, is "If the value of this actor's Role variable is equal to ROLE_Authority, then this actor's Weapon variable should be reliably replicated to all clients for whom this actor is relevant".

A replication condition may be any expression that evaluates to a value of True or False (that is, a boolean expression). So, any expression you can write in UnrealScript will do--including comparing variables, calling functions, and combining conditions using the boolean !, &&, ||, and ^^ operators.

An actor's Role variable generally describes how much control the local machine has over the actor. ROLE_Authority means "this machine is the server, so it is completely authoritative over the proxy actor". ROLE_SimulatedProxy means "this machine is a client, and it should simulate (predict) the physics of the actor". ROLE_DumbProxy means "this machine is a client and it should not do any simulation on this proxy actor". Role is described in more detail in a later section, but the quick summary is this:

  • if ( Role==ROLE_Authority ): means "If I am the server, I should replicate this to clients".
  • if( Role<ROLE_Authority ): means "If I am a client, I should replicate this to the server.

The following variables are very often used in replication statements because of their high utility:

  • bIsPlayer: Whether this actor is a player. Is True for players, false for all other actors.
  • bNetOwner: Whether this actor is owned by the client for whom the replication condition is being evaluated. For example, say Fred is holding a DispersionPistol, and Bob isn't holding any weapon. When the DispersionPistol is being replicated to Fred, its bNetOwner variable will be True (because Fred owns the weapon). When it is being replicated to Bob, its bNetOwner variable will be False (because Bob does not own the weapon).
  • bNetInitial: Valid only on the server side, i.e. if Role==ROLE_Authority. Indicates whether this actor is being replicated to the client for the first time. This is useful for clients with Role==ROLE_SimulatedProxy, because it enables the server to sent their location and velocity just once, with the client subsequently predicting it.

Replication condition guidelines:

  • Since variables are typically replicated one-way (either from the client to the server, or from the server to the client, but never both), all replication conditions generally start with a comparison of Role or RemoteRole, i.e. if( Role==ROLE_Authority ) or if( RemoteRole<ROLE_SimulatedProxy ). If a replication condition doesn't contain a comparison of Role or RemoteRole, there is probably something wrong with it.
  • Replication conditions are evaluated very, very frequently on the server during network play. Keep them as simple as possible, but no simpler.
  • While replication conditions are allowed to call functions, try to avoid that because it could be a big slowdown.
  • Replication conditions shouldn't have any side-effects, because the network code may choose to call them at any time including times when you don't expect. For example if you do something like if( Counter++ > 10 )..., good luck trying to figure out what the hell is going to happen..

Variable Replication

Update mechanism

After every tick, the client and the server check out all actors in their relevant set. All of their replicated variables are examined to see if they have changed since the previous update, and the variables' replication conditions are evaluated to see if the variables need to be sent. As long as there is bandwidth available in the connection, those variables are sent across the network to the other machine.

Thus, the client receives updates of the "important" events that are happening in the world, which are visible or audible to that client. The key points to remember about variable replication are:

  • Variable replication occurs only after a tick completes. Therefore, if in the duration of a tick, a variable changes to a new value, and then it changes back to its original value, then that variable will not be replicated. Thus, clients only hear about the state of the server's actor's variables after its tick completes; the state of the variables during the tick is invisible to the client.
  • Variables are only replicated when they change, relative to their previously known value.
  • Variables for an actor are only replicated to a client when they are in the client's relevant set. Thus, the client does not have accurate variables for actors that are not in his relevant set.
  • UnrealScript has no concept of global variables; so they only variables that can be replicated are instance variables belonging to an actor.

Variable type notes

  • Vectors and Rotators: To improve bandwidth efficiency, Unreal quantizes the values of vectors and rotators. The X,Y,Z components of vectors are converted to 16-bit signed integers before being sent, thus any fractional values or values out of the range -32768..32767 are lost. The Pitch,Yaw,Roll components of vectors are converted to bytes, of the form (Pitch>>8)&255. So, you need to be careful with vectors and rotators. If you absolutely must have full precision, then use int or float variables for the individual components; all other data types are sent with their complete precision.
  • General structs are replicated by sending all of their components. A struct is sent as an "all or nothing" type of thing.
  • Arrays of variables can be replicated, but only of the size of the array (in bytes) is less than 448 bytes.
  • Arrays are replicated efficiently; if a single element of a large array changes, only that element is sent.

Function Call Replication

Remote Routing Mechanism

When an UnrealScript function is called during a network game, and that function has a replication condition, the condition is evaluated and execution progresses as follows:

  • If the function's replication condition evaluates to True, the function call is sent to the machine on the other side of the network connection for execution. In other words, the function's name, and all of its parameters, are crammed together into a packet of data, and transmitted to the other machine for later execution. When this occurs, the function returns immediately and execution continues on. If the function were declared to return a value, then its return value is set to zero (or the equivalent of zero in some other type, i.e. 0,0,0 for vectors, None for objects, etc). Any out parameters are left unaffected. In other words, UnrealScript never sits around waiting for a replicated function call to complete, so it can never deadlock. Rather, replicated function calls are sent off for the remote machine to execute, and the local code continues executing.
  • If the replication condition evaluates to False, the function is executed normally on the local machine.

Unlike replicated variables, a function call on an actor can only be replicated to the player who owns that actor. So, replicated functions are only useful in subclasses of PlayerPawn (i.e. players, who own themselves) and subclasses of Inventory (i.e. weapons and pickup items, who are owned by the player who is currently carrying them). That is to say, a function call can only be replicated to one actor (the player who owns it); they cannot be multicast.

Unlike replicated variables, replicated function calls are sent to the remote machine immediately when they are called, and they are always replicated regardless of bandwidth. Thus, it is possible to flood the available bandwidth if you make too many replicated function calls. Replicated functions suck away however much bandwidth is available, and then whatever bandwidth is left over is used for replicating variables. Therefore, if you flood the connection with replicated functions, you can starve the variables, which visually results in not seeing other actors update, or seeing them update in an extremely choppy motion.

In UnrealScript, there are no global functions, so there is no concept of "replicated global functions". A function is always called in the context of a particular actor.

Replicated Function Calls vs. Replicated Variables

  • Too many replicated functions can flood the available bandwidth (because the are always replicated, regardless of available bandwidth), whereas replicated variables automatically are throttled and parceled out according to bandwidth available.
  • Function calls are replicated only during UnrealScript execution when they are actually called, whereas variables are replicated only at the end of the current tick when no script code is executing.
  • Function calls on an actor are only replicated to the client who owns that actor, whereas an actor's variables are replicated to all clients for whom that actor is relevant.

Quantization Gotchas

To improve bandwidth efficiency, Unreal quantizes the values of vectors and rotators. The X,Y,Z components of vectors are converted to 16-bit signed integers before being sent, thus any fractional values or values out of the range -32768..32767 are lost. The Pitch,Yaw,Roll components of vectors are converted to bytes, of the form (Pitch>>8)&255. If you need complete accuracy on rotators and vectors, send them as individual float or int components.

Actor Roles

The Actor class defines the ENetRole enumeration and two variables, Role and RemoteRole, as follows:

// Net variables.
enum ENetRole
{
    ROLE_None,            // Means the actor is not relevant in network play.
    ROLE_DumbProxy,       // A dumb proxy.
    ROLE_SimulatedProxy,  // A simulated proxy.
    ROLE_AutonomousProxy, // An autonomous proxy.
    ROLE_Authority,       // The one authoritative version of the actor.
};
var ENetRole Role;
var(Networking) ENetRole RemoteRole;

The Role and RemoteRole variables describes how much control the local and remote machines, respectively, have over the actor:

  • Role==ROLE_DumbProxy means the actor is a temporary, approximate proxy which should not simulate any physics at all. On the client, dumb proxies just sit around and are only moved or updated when the server replicates a new location, rotation, or animation information.
    • This situation is only seen in network clients, never for network servers or single-player games.
  • Role==ROLE_SimulatedProxy means the actor is a temporary, approximate proxy which should simulate physics and animation. On the client, simulated proxies carry out their basic physics (linear or gravitationally-influenced movement and collision), but they don't make any high-level movement decisions. They just go.
    • This situation is only seen in network clients, never for network servers or single-player games.
  • Role==ROLE_AutonomousProxy means the actor is the local player. Autonomous proxies have special logic built in for client-side prediction (rather than simulation) of movement.
    • This situation is only seen in network clients, never for network servers or single-player games.
  • Role==ROLE_Authority means this machine has absolute, authoritative control over the actor.
    • This is the case for all actors in single-player games.
    • This is the case for all actors on a server.
    • On a client, this is the case for actors that were locally spawned by the client, such as gratuitous special effects which are done client-side in order to reduce bandwidth usage.

On the server side, all actors have Role==ROLE_Authority, and RemoteRole set to one of the proxy types. On the client side, the Role and RemoteRole are always the exactly reversed relative to the server's value. This is as expected from the meaning of Role and RemoteRole.

Most of the meaning of the ENetRole values is defined by the replication statements in the UnrealScript classes such as Actor and PlayerPawn. Here are several examples of how the replication statements define the meanings of the various role values:

  • The Actor.AmbientSound variable is sent from the server to clients because of this replication definition in the Actor class: reliable if( Role==ROLE_Authority ) AmbientSound;
  • The Actor.AnimSequence variable is sent from the server to clients, but only for actors rendered as meshes, because of this replication definition in the Actor class: unreliable if( DrawType==DT_Mesh && (RemoteRole<=ROLE_SimulatedProxy) ) AnimSequence;
  • The client replicates his Fire and AltFire function calls to the server because of this replication definition in the PlayerPawn class: unreliable if( Role<ROLE_Authority ) Fire, AltFire;
  • The server sends clients the Velocity of all simulated proxies when they are initially spawned and all moving brushes because of this replication definition in the Actor class: unreliable if( (RemoteRole==ROLE_SimulatedProxy && (bNetInitial || bSimulatedPawn)) || bIsMover ) Velocity;

By studying the replication statements in all of the UnrealScript classes, you can understand the inner workings of all of the roles. There is really very little "behind-the-scenes magic" going on with respect to replication: At a low C++ level, the engine provides a basic mechanism for replicating actors, function calls, and variables. At the high UnrealScript level, the meanings of the various network roles are defined by specifying what variables and functions should be replicated based on the various roles. So, the meaning of the roles is almost self-defining in UnrealScript, with the exception of a small amount of behind-the-scenes C++ logic which conditionally updates physics and animation for simulated proxies.

What Exactly Is Happening Behind The Scenes

The answer is for licensees to do a "Find in Files" in Visual C++ for all occurrences of "ROLE_" in the C++ and UnrealScript files. While documentation provides a general understanding of how network roles are interpreted, the exact impact of roles on all aspects of the code can only be completely described by the source code.

Simulated Functions

On the client side, many actors exist in the form of "proxies", meaning approximate copies of actors created by the server, and sent to the client to provide a visually and aurally reasonable approximation of what the client sees during gameplay.

On the client, these proxy actors are often moving around using client-side physics and affecting the environment, so at any time their functions can potentially be called. For example, a simulated proxy TarydiumShard projectile might run into a dumb proxy Tree actor. When actors collide, the engine attempts to call their Touch functions to notify them of the collision. Depending on context, the client desires to execute some of these functions calls, but ignore others. For example, a Skaarj's Bump function should not be called on the client side, because his Bump function attempts to carry out gameplay logic, and gameplay logic should only occur on the server. So, the Skaarj's Bump function should not be called. However, a TarydiumShard projectile's Bump function should be called, because it stops the physics and spawns a client-side special effect actor.

UnrealScript functions can optionally be declared with the "simulated" keyword to give programmers fine-grained control over which functions should be executed on proxy actors. For proxy actors (that is, actors with Role<ROLE_Authority), only functions declared with the "simulated" keyword are called. All other functions are skipped.

Here is an example of a typical simulated function:

simulated function HitWall( vector HitNormal, actor Wall )
{
        SetPhysics(PHYS_None);
        MakeNoise(0.3);
        PlaySound(ImpactSound);
        PlayAnim('Hit');
}

So, "Simulated" means "this function should always be executed for proxy actors".

Player Prediction

Overview

If a pure client-server model were used in Unreal, player movement would be very laggy. On a connection with 300 msec ping, when you push the forward key, you wouldn't see yourself move for 300 msec. When you pushed the mouse left, you wouldn't see yourself turn for 300 msec. This would be really frustrating.

To eliminate client-movement lag, Unreal uses a prediction scheme similar to that pioneered by QuakeWorld. It must be mentioned that the player prediction scheme is implemented entirely in UnrealScript. It is a high-level feature implemented in the PlayerPawn class, rather than a feature of the network code: Unreal's client movement prediction is layered entirely on the general-purpose replication features of the network code.

Inner Workings

You can see exactly how Unreal's player prediction works by examining the PlayerPawn script. Since the code is somewhat complex, its workings are briefly described here.

The approach can best be described as a lock-step predictor/corrector algorithm. The client takes his input (joystick, mouse, keyboard) and physical forces (gravity, buoyancy, zone velocity) into account and describes his movement as a 3D acceleration vector. The client sends this acceleration along with various input related information and his current timestamp (the current value of Level.TimeSeconds on the client side) to the server in a replicated ServerMove function call:

function ServerMove
(
    float TimeStamp,
    float AccelX,
    float AccelY,
    float AccelZ,
    float LocX,
    float LocY,
    float LocZ,
    byte MoveFlags,
    eDodgeDir DodgeMove,
    rotator Rot,
    int ViewPitch,
    int ViewYaw
);

Then the client calls his MoveAutonomous to perform this same identical movement locally, and he stores this movement in a linked list of remembered movements using the SavedMove class. As you can see, if the client never heard anything back from the server, the client would be able to move around with zero lag just as in a single-player game.

When the server receives a ServerMove function call (replicated across the network), the server carries out the exact same movement on the server immediately. It deduces the movement's DeltaTime from the current ServerMove's TimeStamp and the previous one's. In this way, the server is carrying out the same basic movement logic as the client. However, the server might see things slightly different than the client. For example, if there's a monster running around, the client might have thought it was in a different position than the server (because the client is only in rough approximate sync with the server). Thus, the client and the server might disagree about how far the client actually moved as a result of the ServerMove call. At any rate, the server is authoritative, and he is completely responsible for determining the client's position. Once the server has processed the client's ServerMove call, it calls the client's ClientAdjustPosition function which is replicated across the network to the client:

function ClientAdjustPosition
(
    float TimeStamp,
    name newState,
    EPhysics newPhysics,
        float NewLocX,
    float NewLocY,
    float NewLocZ,
    float NewVelX,
    float NewVelY,
    float NewVelZ
);

Now, when the client receives a ClientAdjustPosition call, he must respect the server's authority over his position. So, the client sets his exact location and velocity to that specified by the ClientAdjustPosition call. However, the position the server specifies in ClientAdjustPosition reflects the client's actual position at some time in the past. But, the client wants to predict where he is supposed to be at the present moment. So, now the client goes through all of the SavedMove's in his linked list. All moves earlier than the ClientAdjustPosition call's TimeStamp are discarded. All moves that occurred after TimeStamp are then re-run by looping through them and calling MoveAutonomous for each one.

This way, at any point in time, the client is always predicting ahead of what the server has told him by an amount of time equal to half his ping time. And, his local movement is not at all lagged.

Advantages

This approach is purely predictive, and it gives one the best of both worlds: In all cases, the server remains completely authoritative. Nearly all the time, the client movement simulation exactly mirrors the client movement carried out by the server, so the client's position is seldom corrected. Only in the rare case, such as a player getting hit by a rocket, or bumping into an enemy, will the client's location need to be corrected.

The GameInfo class

The UnrealScript GameInfo class implements the game rules. A server (both dedicated and single-player) has one GameInfo subclass, accessible in UnrealScript as Level.Game. For each game type in Unreal, there is a special GameInfo subclass. For example, some existing classes are DeathmatchGame, SinglePlayer, TeamGame.

A client in a network game does not have a GameInfo. That is, Level.Game==None on the client side. Clients should not be expected to have a GameInfo because the server implements all of the gameplay rules, and the generality of the code calls for the client not knowing what the game rules are.

GameInfo implements a broad set of functionality, such as recognizing players coming and going, assigning credit for kills, determining whether weapons should respawn, and so on. Here, we will only look at the GameInfo functions which are directly related to network programming.

InitGame

event InitGame( string[120] Options, out string[80] Error );

Called when the server (either in network play or single-player) is first started up. This gives the server the opportunity to parse the startup URL options. For example, if the server was started with "Unreal.exe MyLevel.unr?game=unreali.teamgame", the Options string is "?game=unreali.teamgame". If Error is set to a non-empty string, the game fails with a critical error.

PreLogin

event PreLogin( string[120] Options, out string[80] Error );

Called immediately before a network client is logged in. This gives the server an opportunity to reject the player. This is where the server should validate the player's password (if any), enforce the player limit, and so on.

Login

event playerpawn Login( string[32] Portal, string[120] Options, out string[80] Error, class<playerpawn> SpawnClass );

The Login function is always called after a call to PreLogin which does not return an error string. It is responsible for spawning the player, using the parameters in the Options string. If successful, it should return the PlayerPawn actor it spawned.

If the Login function returns None indicating that the login failed, then it should set Error to a string describing the error. Failing a Login should be used only as a last resort. If you are going to fail a login, it is more efficient to fail it in PreLogin rather than Login.

Bandwidth Performance Tips

Optimization goal

The goal here is to maximize the amount of visibly important detail that is sent with a given bandwidth limit. With the bandwidth limit determined at runtime, your goal in writing scripts for actors that are used in multiplayer games is to keep bandwidth use to a minimum. The techniques we use in our scripts include:

  • Use ROLE_SimulatedProxy and simulated movement whenever possible. For example, nearly all of the Unreal projectiles use ROLE_SimulatedProxy. The one exception is the Razorjack alt-fire blades, which the player can steer during gameplay, thus the server must continually update the position to clients.
  • For quick special effects, spawn the special effects actors purely on the client side. For example, many of our projectiles use a simulated HitWall function to spawn their effects on the client-side. Since these special effects are just decorative rather than affecting gameplay, there is no drawback to doing them completely on the client side.
  • Fine tune the default NetPriority for each class. Projectiles and players need to have high priorities, purely decorative effects can have lower priorities. The defaults that Unreal provides are good first-pass guesses, but you can always gain some improvement by fine-tuning them.
  • When an actor is first replicated to a client, all of its variables are initialized to their class default values. Subsequently, only variables that differ from their most recent known values are replicated. Thus, you should design your classes so that as many variables as possible are automatically set to their class defaults. For example, if an actor always should have a LightBrightness value of 123, there are two ways you can make that happen: (1) set the class default's value of LightBrightness to 123, or (2) in the actor's BeginPlay function, initialize LightBrightness to 123. The first approach is more efficient, because the LightBrightness value will never need to be replicated. With the second approach, the LightBrightness needs to be replicated each time an actor first becomes relevant to the client.

Traffic Monitoring

To monitor network traffic, licensees can compile the engine with:

#define DO_SLOW_GUARD 1

This enables network statistic gathering. Then, comment out the line Suppress[#]=DevNetTraffic in Unreal.ini. Then, a complete summary of network data received by the machine will be written to the log. In other words, do this on the client side to see the data sent by the server. Do it on the server side to see the data sent by the client. The data is written to the log in a very verbose format, giving timestamps for all packets, along with a summary of all replicated actors, variables, and function calls.

Network Driver Implementation

Plug-in Network Drivers

Unreal's network support is generalized through a plug-in interface based on the C++ UNetDriver class. The Unreal engine deals with all networking issues through the UNetDriver class, and dynamically loads the appropriate network driver specified in Unreal.ini, which defaults to:

[Engine.Engine]
NetworkDevice=IpDrv.TcpNetDriver

Unreal's current Internet support is the UDP network driver, named TcpNetDriver. However, it is quite possible to support new kinds of networks by creating new subclasses of UNetDriver, and using the TcpNetDriver source as a guideline for how to implement the new functions. In fact, the Maverick Software team has created an AppleTalk driver for the Mac version of Unreal using this interface.

The network driver is responsible for opening connections, closing connections, sending data, and receiving unreliable data. However, the contents of the data is defined by the Unreal Wire Protocol, and is completely opaque to the network driver itself. Thus, UNetDriver has no idea what information it's communicating, it just sends and receives it blindly. Its characteristics are as follows:

  • Connection-oriented. UNetDriver is responsible for maintaining a list of connections defined by UNetConnection subclasses. When UNetDriver is layered on top of a connectionless protocol (such as UDP), it must contain its own internal logic for maintaining connections, handling their timeouts, etc.
  • UNetDriver is packet-oriented, rather than stream-oriented. All data sent and received through UNetDriver exists in discrete packets of size 0...MAX_PACKET_SIZE.
  • Packets are sent unreliably. All reliable sending and receiving of packets is performed at a higher level which is invisible to UNetDriver. A packet sent from one machine to another might not arrive, might arrive once, or might arrive multiple times. Additionally, packets might arrive out of order or with significant delay.
  • Packets are never corrupted. When a packet is received, it is guaranteed that the size and contents received are identical to the size and contents that were sent.

Internet Driver

Unreal's Internet support is based on UDP, the standard Internet protocol for unreliable, connectionless communication. All Unreal gameplay coordination between a client and server goes through one constant, unchanging pairing of UDP addresses (where the client and server each have a 32-bit Internet address and a 16-bit port number). The default port number can be seen in Unreal.ini.

Thus, Unreal's UDP packets are extremely easy to proxy, and they can be proxied transparently without the proxy having to know anything about their contents.

Unreal Wire Protocol

The Unreal Wire Protocol defines the contents of packets as a stream of bits. The protocol deals with a variety of concepts, such as replicating variables and functions, creating actors, destroying actors, transmitting files, and exchanging packets reliably and unreliably (both types are used in various circumstances).

The protocol will undergo some major changes in the coming months to improve efficiency and reliability, so it would not be useful to document it here and now.

It must be mentioned that the protocol is closely tied to the Unreal package file format described in the Package Documentation so it will be quite difficult to parse and do anything useful with.

UnrealScript: UdpLink, TcpLink

Unreal's TcpLink and UdpLink scripts enable you to write, in UnrealScript, code for actors which are able to communicate with external programs. These are the optimal way of extending Unreal with cool new networking features. For example, some of the cool things one could do are:

  • Write "event gateways" which forward events (such as players pressing buttons, walking into zones, etc) to other servers.
  • Write server-side programs to interface the server to master servers, statistic trackers, and so on.
  • Write server-side programs to maintain persistent player statistics or inventory. For example, you could write a Java/C++ database style "player account manager" program that keeps track of player names, passwords, and their inventory for players. Then, you can write an UnrealScript class to interface to the account manager by TCP or UDP and validate players that are trying to log in. See GameInfo.Login, TcpLink, and UdpLink for ideas.
  • Write client-side chat programs.

The Present and The Future

The words one might use to describe Unreal's networking architecture are "powerful", "general", "complex" and "hard to master". The architecture went through a tremendous amount of evolution during the game's development, to arrive at the current solution which is a balance between power, simplicity, and the pragmatic need to solve certain problems.

The ideal networking architecture, from an ease-of-programming standpoint, is the pure client-server model, where the client truly acts as a dumb rendering terminal, and receives a display list from the server. However, as discussed previously, the analysis of the rate of bandwidth improvement versus hardware improvement indicates that we live in a world where the pure client-server model will be impractical for the foreseeable future.

Thus, the ability to provide rich client-side simulation of physics and script execution in general is tantamount in any next-generation networking engine. In Unreal we have defined a generalized networking model based on the generalized replication of objects, object variables, and object function calls. This model provides rich simulation capabilities without being hardcoded to any particular simulation model.

I'm pretty sure that this broad networking architecture for Unreal is the right one. Some of the research areas I investigated while determining the current direction include database replication, Unix RPC, the CORBA distributed object model, and Java RPC. My general conclusion is that, though there are some cool ideas out there, nobody else is really pushing the limitations of Internet technology as much as game developers. The same can probably be said about other research areas, such as real-time 3D.

As for the future, there are a lot of areas where this model can be extended and improved. The addition of multicast replicated functions would increase the amount of simulation that can be done without variable replication. An extended peer proxy model could enable the creation of a server backbone enabling the realistic real-time flow of NPC objects between servers. The replicated object model could be extended to other areas of the engine, such as the user-interface and chat system. There is a tremendous amount of promise for this architecture to be applied to much richer, more persistent game environments such as the worlds of Ultima Online, while retaining the distributed, user-modifiable nature of the system. In the coming years, networking will be an extremely interesting area of game development.

-Tim Sweeney

End


True-Type Font Importing

True-Type Font Importing in Unreal

Jack Porter
Epic Games, Inc.
http://www.epicgames.com/

Audience: Licensees and mod-makers wanting to use Windows true-type fonts in the Unreal engine.
Last Updated: 08/10/99

Introduction

The TTF importer allows you to convert a Windows true-type font of a particular size into an Unreal font which you can then use to draw in-game text. The Unreal engine now has support for rendering anti-aliased fonts, and the TTF importer allows you to import the anti-aliasing from a Windows font.

Unreal has had the ability to import true-type fonts for use in-game since version 224, although there was a bug in the 224 font importer code which meant all the fonts you imported ended up as MS Sans Serif. Be sure you're using Unreal 225f or later, or you may have problems.

Importing Fonts into a .u File

You can import regular Unreal font textures into .u files using a #exec command, which instructs ucc make to import the font as the .u package is created. For example:

#exec Font Import File=Textures\MedFont.pcx Name=MedFont

Once the font is imported, you can refer to it as Font'MedFont' from inside UnrealScript.

To import a TTF, the true-type font first needs to be installed and accessible as a Windows font. If you've got a *.TTF file, you'll have to start by copying the font file into the Fonts control panel. Once this is done, you can import that TTF into a .u file, when ucc make creates the .u package, also by using a #exec command:

#exec new TrueTypeFontFactory Name=Tahoma30 FontName="Tahoma" Height=30 AntiAlias=1 CharactersPerPage=32

  • The Name parameter specifies the name of the font. In this case, I can refer to the font as Font'Tahoma30' from Script
  • The FontName parameter specifies the Windows name of the font to be imported. For example "Comic Sans MS", "Arial" or "Earth Normal".
  • The Height parameter is the height of the font in pixels, which I give to Windows' CreateFont() API call.
  • If AntiAlias is 1, the TTF importer requests an anti-aliased font from Windows with the ANTIALIASED_QUALITY flag to CreateFont(). If it is 0, I pass NONANTIALIASED_QUALITY. See below for limitations of importing fonts with anti-aliasing.
  • The CharactersPerPage parameter specifies the number of characters per 256x256 pixel font texture. The default is 64. The TTF importer will give you an error if it runs out of space when drawing characters on the font pages. If you get the error "Font vertical size exceeded maximum of 256", you should half the CharactersPerPage parameter.
  • Two other parameters - XPad and YPad, add extra space between characters. These default to 1. Some fonts require extra space between characters, as Windows doesn't report their actual spacing requirement correctly.

Importing Anti-aliased Fonts

I've found that Windows only honors the ANTIALIASED_QUALITY parameter to CreateFont() when the following conditions are met:

  • Running Windows NT4. I don't know why. The ANTIALIASED_QUALITY flag is undocumented.
  • Running in 16-bit or 32-bit color depth
  • Specifying a Height above a certain size

This last limitation is apparently coded into the TTF file in a table called the GASP Table. I found this program which removes the GASP table from a TTF file, and allows you to request an anti-aliased font at any font size. But with some fonts, the letter i disappears to nothing at small font sizes. Anti-aliasing looks best with large fonts.

Importing Fonts into a .utx File

Because importing the fonts requires that the font be installed and that the machine doing the importing is configured in a way which can import anti-aliased fonts, we tend to import our fonts directly into a UTX file. This way, they're imported only whenever we change or add to the fonts. To do this, you need to run UnrealEd and choose the Window|Log menu option. From the UnrealEd log window, you can type any #exec command - just type the command without the "#exec" part. For example:

new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=Tahoma30 FontName="Tahoma" Height=30 AntiAlias=1 CharactersPerPage=32

The Package parameter specifies the name of the UTX package to import the font into. If you then choose Browse Textures over on the right hand side of UnrealEd, you'll be able to see the fonts you've just imported. You can then press the Save button to save the UTX texture package.

A way to automate this is to create a text file with a number of commands you would type into the log window. Here's a copy of a file called uwindowfonts.txt, which imports a number of fonts into a package called UWindowFonts.utx. To run it, I type "exec uwindowfonts.txt" from the UnrealEd log window.

new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=Tahoma10 FontName="Tahoma" Height=10 AntiAlias=0
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=Tahoma20 FontName="Tahoma" Height=20 AntiAlias=1
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=Tahoma30 FontName="Tahoma" Height=30 AntiAlias=1 CharactersPerPage=32
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=TahomaB10 FontName="Tahoma Bold" Height=10 AntiAlias=1
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=TahomaB20 FontName="Tahoma Bold" Height=20 AntiAlias=1 XPad=2
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=TahomaB30 FontName="Tahoma Bold" Height=30 AntiAlias=1 CharactersPerPage=32 XPad=2
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=UTFont12 FontName="Verdana" Height=12 AntiAlias=0
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=UTFont24 FontName="Arial Narrow" Height=24 AntiAlias=1
new TrueTypeFontFactory PACKAGE="UWindowFonts" Name=UTFont40 FontName="Arial Bold" Height=40 CharactersPerPage=32 AntiAlias=1
OBJ SAVEPACKAGE PACKAGE="UWindowFonts" FILE="..\Textures\UWindowFonts.utx"

The final line automates the saving of the .utx file.

Referring to Fonts in a .utx File with OBJ LOAD

Once you have a font in a UTX, there are a number of ways you can access it from Script. You can use a #exec OBJ LOAD directive to force the script compiler to load the UTX package at script compile time.

This example, from a script file in Botpack.u package, loads the AmmoCount.utx file and includes its contents inside the Botpack.u package. The Ammocount.utx file doesn't need to be distributed because its contents are included inside the .u file.

#exec OBJ LOAD FILE=Textures\Ammocount.utx PACKAGE=Botpack.Ammocount

This example loads the UTX into memory and makes the fonts it contains accessible to script, but the UTX still needs to be distributed as its contents do not get included in the .u file. Note the PACKAGE parameter is not the name of the current package being compiled - that indicates that the package should just be loaded into memory so the script compiler can refer to objects inside the package.

#exec OBJ LOAD FILE=..\Textures\UWindowFonts.utx PACKAGE=UWindowFonts

In both cases, the fonts are accessible with the Font'fontname' syntax.

Loading Fonts at Runtime from a .utx File

You can also load the fonts at runtime, using DynamicLoadObject. This is useful if you have a number of fonts you may want to use depending on resolution etc.

local Font F;
local string S;
local int FontSize;
FontSize = 30;
S = "uwindowfonts.tahoma"$string(FontSize);
F = Font(DynamicLoadObject(S, class'Font'));

This is the method we use in Unreal Tournament to dynamically load fonts for the GUI and HUD.

End


Unrealed Reference Credits

Unrealed Reference
Authors/Contributors

Roger [666] Bacon
rogerbacon@mindless.com

Bane
bane@unrealized.com

BEG

Alexander Brandon
abrandon@uswest.net

Shane Caudle
Shane@Scott.Net

cyberg][dan
dan@catapultic.com

Ray Davis
ca@planetunreal.com

Tony Garcia (a.k.a. Wolf)
Wolf@gamedesign.net

Isolation

Tomasz ‘Millennium’ Jachimczak
tomasz@planetunreal.com

JbP
jbp@planetunreal.com

Matt Murphy
utcity@planetunreal.com

Nemesis
nemesis@teamanarchy.com

Mark Poesch

Steven Polge

Jack Porter
jack@epicgames.com

Brandon Reinhart
brandon@epicgames.com

Sharky
jneta@ez-net.com

Silencer

Squidi
howard@mailer.fsu.edu

TheShrew
zmstauffer@prodigy.net

Tim Sweeney

Trevelyan
connors@nnex.net

Verxion
jc83151@goodnet.com

Valiant
valiant@twistedpine.com

Chris Willis
chris.willis@sierra.com


UnrealEd 2d Actor Menu

UnrealEd 2d View Menu - Actor

This is the default view. It will show each actors unique icon and meshes for actors that have them

This view will show all actors as generic icons.

This view will show the radius of the selected actor. It helps for setting the collision radius of triggers, and the light radius of lights. But that's another tutorial :)

This view hides all actors, showing only the brushes.

Wolf's Unreal


UnrealEd 2d View Menu

This menu will let you select which brushes are shown in the 2D view. The image above shows the default setting and this is where you will usually leave it. The check mark indicates it is set on. It should be self-evident what each does. The Active Brush is the Red brush and moving brushes are special brushes (like doors, lifts*). Backdrop and Coordinates do not apply, and Paths is used for showing the paths defined with path nodes (the paths that bots and monsters use to get round the level)--again, a future tutorial.

* Note: Moving brushes are ALWAYS rendered in "wire-frame" mode.

Wolf's Unreal


UnrealED Keyboard Shortcuts

Keyboard Shortcuts
Special mouse clicking combinations

A + right mouse click: Add actor there.
L + right mouse click: Add light there.

Shortcut keys

Delete: Delete selected actors.
1, 2, 3: Slow, medium, and fast movement speed.
F1: Help.
F4: Actor properties.
F5: Surface properties.
F6: Level properties.
F7: Compile changed scripts.
F8: Rebuilder.
B: Toggle brush visibility in the current viewport.
H: Toggle actor visibility in the current viewport.
P: Toggle player controls or real-time update of the viewport.
Shift-A: Select all actors.
Shift-B: Select all surfaces belonging to the selected surfaces' brushes.
Shift-C: Select adjacent coplanar polygons.
Shift-D: Duplicate selected actors.
Shift-F: Select adjacent floors.
Shift-G: Select surfaces belonging to the selected surfaces' groups.
Shift-I: Select surfaces with items matching the selected surfaces' items.
Shift-J: Select all adjacent surfaces(flood-fill).
Shift-L: Look ahead in the current viewport (no pitch or roll).
Shift-M: Memorize selected surfaces.
Shift-N: Select none.
Shift-O: Select surfaces from intersection of selected surfaces and memory.
Shift-P: Select all surfaces.
Shift-Q: Reverse the set of selected surfaces.
Shift-R: Recall memorized surface selection.
Shift-T: Select all surfaces which use the current texture.
Shift-U: Select union of selected surfaces and memorized surfaces.
Shift-W: Select adjacent wall surfaces (flood-fill).
Shift-X: Select exclusive or of selected surfaces and memorized surfaces.
Shift-Y: Select adjacent slanted surfaces.
Shift-Z: Select none.
Ctrl-A: Add brush to world.
Ctrl-B: Load brush.
Ctrl-C: Copy.
Ctrl-D: Deintersect brush with world.
Ctrl-E: Save as.
Ctrl-L: Save level.
Ctrl-O: Load level.
Ctrl-P: Play level.
Ctrl-N: Intersect brush with world.
Ctrl-R: Redo.
Ctrl-S: Subtract brush from world.
Ctrl-V: Paste.
Ctrl-W: Duplicate.
Ctrl-X: Cut.
Ctrl-Z: Undo.

Wolf's Unreal


Properties Menu

Properties Window

This is the a generic Properties Window in UnrealEd. You can open this dialog for ALL actors. Though each Properties window looks the same there are specific fields that pertain only to that actor and those fields are unique to that actor's Properties Windows.

This is the Properties Window for one of Barrels in out level. Note the similarities with the generic window above, but note the differences. I have highlighted the actor-specific fields, which are the ones you should be concerned with for this actor.

Here I have highlighted the actor-specific fields for a light actor.
Note: Light Color and Lighting appear in all the Properties windows, but most actors will not emit light.

Wolf's Unreal


Rebuilder

This is the Rebuilder window. Some of the numbers in this window will be useful later on when you have more experience with level editing. These properties won't update until you rebuild.
The easiest (and best) way to rebuild is to simply click the Rebuild Geometry button. If you leave everything else at its default setting it will automatically rebuild the geometry, run the BSP and render the lighting. If Auto-BSP is checked in the Geometry window, then it will automatically run, and if Auto-Lighting is checked in the BSP window then it will automatically run. The BSP "cuts up" the polygons for rendering, and Lighting renders the lighting for the level. There are individual settings for each of these accessed by the tabs at the top of the window, but it is best to just leave them alone.

These are the other tabs in the Rebuilder window.

Wolf's Unreal