Legacy:Traversing Classes

From Unreal Wiki, The Unreal Engine Documentation Site
Jump to navigation Jump to search

This page is an extension to Package. I will attempt to explain the basic methods used in accessing and manipulating variables, functions, properties, etc. in other classes.

This tutorial is designed for new scripters. After reading this document, hopefully you will be able to:

  • Recognize if an arbitrary class is accessible from within the class you're currently editing
  • Retrieve the values of variables defined in other classes
  • Modify and save variables in other classes from within the class you're currently editing
  • Execute functions in other classes
  • Spawn new instances of other classes
  • Gain valid references to any class from within any other class in UT (this one can be tough)

This tutorial assumes you are already familiar with:

After reading this tutorial, you should next learn about:

  • Working with Iterators
  • Manipulating return types for functions
  • Working with static/default
  • When to override functions

Variable Types

Defining Variables

MyVar = SomeOtherVariable;

Here I'm setting the value of the MyVar variable to the value of SomeOtherVariable. Pretty simple. Well, that's fine if SomeOtherVariable is in our own class, but what if SomeOtherVariable is in another class? Actually, it depends a lot on where you are attempting to access this variable from, and what you want to use it for. Another big part of how we'll do this is exactly what type of variable it is, such as a regular variable, like the ones you define at the top of your class's script, a local variable, or argument variables. Let's define these real quick.

Global variable
Defined at the beginning of the class, before any functions, after the class statement and any #exec lines. Global variables are public variables, and can be accessed and modified by any other class. These variables are the only kind that can be config variables (can be saved to an .ini file). Likewise, only global variables can be defined in defaultproperties{ }. Global variables can be used throughout the class that they are defined in, as well as any child classes (without being redefined in those child classes).
Global variables look like this:
         var int NumUsers;
         var config bool bTournament;
         var class<Pawn> MyPawnType;
Local variables
Local variables are defined in the beginning of functions, before any statements are executed. Local variables may only be used within the function that they are defined in, and will be reset to their null value (0 for integers, "" for strings, None for actors, false for booleans, 0.00 for floats, etc.) each time that function is called. Local variables are inaccessible to functions outside of the function it is contained in, including functions in the same class or child classes. Local variables look like this:
         local int i;
         local PlayerPawn P;
         local Names[128] UserName;
Argument variables
Argument variables are essentially local variables, which are declared and assigned a value in one step. Argument variables are the variables you use in function definitions, such as:
function DoSomething(string ArugmentVariableString, int ArgumentVariableInt, bool ArgumentVariableBool)
Argument variables, like local variables, may only be used in the function that they are defined within. You do not need to declare argument variables manually in your function ( i.e. local bool bSomething )

Working with Variables

The key to learning how to effectively work with variables and functions in other classes is to understand the difference between a class and an object of that class. Let's say we're working with the HealthVial class, and we want to get the value of the "HealingAmount" variable from that class. Let's say we're doing this from a mutator (since most who read this will probably be working on their first or second mutator).

There are essentially two methods to do this, depending on whether we only need the starting value of the HealingAmount variable, or whether we need the "real-time" value of the HealingAmount variable.

It's helpful to think of classes (classes in this case being the actual script that you write) as templates for objects. Inside the game engine, when it is time to create your object, the engine looks at the class for your object, and creates an object that has all of the parameters and characteristics of your script. If a default value for any of your global variables is listed, it assigns those values to the object. From that point on, the global variable values may be changed or affected by methods within that object, or other objects within the game. The original default value, however, is not affected by any changes made to the object that was spawned.

To understand what an "instance" is, and how to access instances of objects, think about a car lot. Imagine you go to a car dealer, and give the dealer a list of parameters for the car that you want. Let's say you are looking for a Ford Mustang, manual transmission, red exterior, white interior, convertible. So, the dealer thinks for a minute, reaches under the desk, and hands you a set of keys and tells you your car is outside in the lot. You walk outside where there are thousands of cars. Of those thousands of cars, there are a few hundred Mustangs. Of those few hundred Mustangs, there are about 100 that have red exteriors. Of those, there a few dozen that have white interiors.....so on and so forth. Your key will only work in ONE of these cars, but the dealer hasn't told you which one. Since it isn't really feasible to try the key in every one of those thousands of cars, you're stuck.

This is very similar to how objects and classes work inside the game engine. The description you gave the dealer, describing the characteristics of the car you're looking for is like a class. Attempting to access a variable in a class without first having a valid reference to an object is like the dealer handing you they keys to the car without telling you which car they belong to, then telling you to go start it up. You won't be able to start it up until the dealer tells you exactly which car the keys belong to.

OK, so now we know the difference between a class and an object in a very basic abstract sense, but what does that *really* mean?? How does knowing this help me write a mod? Where does the madness end?

woot! I wrote another paragragh :) I swear I'm going to finish this document one day :P

The first, and easiest way to do this, is to call an iterator function.

<uscript> var BigHealthVial BHV1; var BigHealthVial BHV2; var BigHealthVial BHV3;

function PostBeginPlay() { local HealthVial HV;

foreach AllActors(class'HealthVial', HV) { if (HV.HealingAmount > 25) { if (BHV1 == None) BHV1 = HV; else if (BHV2 == None) BHV2 = HV; else if (BHV3 == None) BHV3 = HV; } }

log("Found all the Big Health Vials!") } </uscript>

To be continued Evolution

the following is a cut-n-paste from a conversation I had in IRC in which I covered most of the material I was going to write here, so I'm leaving it here (so I don't lose it) until I have time to edit and clean it up a bit:

well, there are two different methods

in method one, you have a reference to an actual object in the game

method 2, you don't have an actual object, but want to access the class anyway

but I think you'd be safer with doing the first way, and accessing ACTUAL health vials that exist in the game
well, you're dealing with instance variables
so you'll need a valid reference to each instance of the class 'HealthVial'
ok, say we're in the game and we have 100 health vials in the level

i want to access HealthAmount variable
maybe something like
if (HealthAmount != 25) { do my code }
so far so good?
but in the game, "MedBox" doesn't exist
"MedBox" is a class
(20:31) (Dorkorama|VolatileHEALTH) "in the game" is refering to the hypothetical, right?
(20:32) (Evolution) no, in the game, referring to "as our code is executing in the game"

only OBJECTS of the class 'MedBox' exist in the game
so to access their variables, we must use a "class reference variable" (read: class-reference variable, or reference-to-object-of-XXClass variable)
so we declare a variable like: var MedBox MB;

to access a variable in an object of a particular class, we must first have a class reference variable to work with
the class reference variable will represent the objects whose variables we want to access

MB is still nothing
because we haven't assigned anything to it
we've only declared it

it means, "whatever we assign to MB, it must be this type of thing"
(20:36) (Evolution) so then we can do an iterator
(20:36) (Evolution) let's use Foreach AllActors
(20:37) (Evolution) the syntax for that is "foreach allactors(<class we're looking for>, <our class variable>)
(20:37) (Evolution) so in this case, it would be "ForEach AllActors(class'MedBox', MB)"

Okay... that searches every actor and if it matches the class we're looking for it's data is copied to the class variable (MB)

when that happens, it will execute whatever code is in the brackets
sooooooo, ForEach AllActors(class'MedBox', MB) { if (MB.HealthAmount != 25) MB.HealthAmount = 25; }
after it executes whatever code is in the brackets, then it will continue searching all the actors for another medbox, then it will do it all over again
(20:41) (Evolution) is HealthVial a subclassof MedBox?
(20:41) (Dorkorama|VolatileHEALTH) No
(20:41) (Evolution) then that wouldn't work
(20:41) (Dorkorama|VolatileHEALTH) Huh?
(20:42) (Evolution) nesting foreach?
(20:42) (Dorkorama|VolatileHEALTH) Yeah
(20:42) (Evolution) avoid that
(20:42) (Dorkorama|VolatileHEALTH) Crud....
(20:42) (Evolution) until you're more familiar with using iterators, anyway
(20:42) (Dorkorama|VolatileHEALTH) bleh....
(20:42) (Evolution) cuz that's a real easy way to make UT grind to a halt
(20:42) (Dorkorama|VolatileHEALTH) 8-\
(20:43) (Evolution) if you're doing the iterator in a singlular function, like PostBeginPlay()
(20:43) (Dorkorama|VolatileHEALTH) I still wonder what's in the latest PURE builds that makes gameplay go slower and slower
(20:43) (Dorkorama|VolatileHEALTH) ANyway...
(20:43) (Evolution) just do two iterators
(20:43) (Evolution) one for medbox, one for healthvial
To be Continued....  →Evolution

Tarquin: Ev, I think this will be a multi-page topic. Do you want to take material from Communication Between Objects for this? (BTW, I think this page title is way better than "Communication Between Objects" )