Communication Between Objects
Objects in UnrealScript often need to be able to discover information about each other in order to interact in a meaningful fashion, here are some examples:
- A weapon needs to know how many charges are left in a powerup.
- In a CTF the flag needs to know the team of the player that just touched it.
- A player needs to know who fired the projectile that just killed them.
- The guided redeemer shell needs to know the orientation of the player guiding it.
This sort of thing is achieved by object references in the code and is probably best explained by example, so lets look at the CTF flag for an example:
(to be continued)
EntropicLqd: Personally, I've always found the term "communication" to be hidiously misleading. To me, communication involves some sort of network or serial comms code. In this case, communication between objects is simply a function call, that has access to some "protected data" (i.e. the attributes of the object the function is being called on. You can replace the word function with method if it makes you happy.
A silly example
Using the BugEyedMonster class from Extending States again, let's suppose that we have a BugEyedQueen, and that when BugEyedMonster is very scared it runs towards her. We might have some sort of function RunToQueen() in BugEyedMonster's script.
The simplest thing might be to look for the Queen when we need to find her, with a foreach iterator: obtain a reference to the Queen, get her location and move towards it.
(example code here...)
This might be fine if the BUM only needs to do this once. However, if the Queen is also moving we have ourselves a case of the "pursuit problem" in maths (the one I could never solve – Tarquin), and BUM needs to know about her location more than once, maybe even every tick. Running a foreach iterator this frequently is too heavy for the engine, and there's a more efficient way of doing it:
Give the BUM a variable of type BugEyedQueen. This in fact means a variable that holds a reference to an object, and the class of the object must be BugEyedQueen, or a subclass. (Remember the peppermill and peppercorn analogy: this variable does not indicate a class, it indicates an object of a certain class.) We could make it so the mapper has to set its value to point to the Queen object in the map:
var() BugEyedQueen MyQueen;
This requires the mapper to think, is bound to lead to confusion, recriminations and spilled milk. Docers often need to think for pammers
- set the variable uneditable by declaring it with just var, no ()
- in one of the begin play functions (PreBeginPlay, PostBeginPlay, I don't know which is appropriate (PostBeginPlay() gets my vote – EntropicLqd)), run the foreach code as above to find the Queen, and set MyQueen to point to it
- whenever we need to find something out about the Queen, simply access local variable MyQueen, for example Destination = MyQueen.Location
I've just realised on reading the bit below that I essentially (and stupidly) rewrote it with BugEyedMonster above. I'll try and merge them later. I'll stick to the reactor example, it's by far the more sensible of the two – Tarquin
maybe a page on PreBeginPlay, PostBeginPlay would be useful, but I don't know what to call it. – Tarquin
have a look at the Creating Actors and Objects page. ("What exactly happens when I spawn an actor?") Is this what you were looking for or should there be more details? – Wormbo
Tarquin: snip from one of my forum posts:
The standard way round this is to only call ForEach once, like this:
Each actor that will need to call Bomb.AddTime gets a:
var bomb myBomb ;
(check my syntax, it might be var class<bomb> etc...)
Then one of the BeginPlay functions calls ForEach.AllActors and sets the local myBomb variable to point to the map's Bomb actor.
Then when you need to access the Bomb actor's function, grab it from myBomb.AddTime.
Comments
Tarquin Excellent idea, including this kind of information. Although, NOOBs will not have clue as to what you're referring to, and, how it would be implemented. I'll tell ya,...being a NOOB sucks. NFG
It's very vague for now, needs major work. – Tarquin
Zedsquared chips in with a more general view:
If you want to have a master actor on your map that exchanges information with other slave actors which can be dynamically created during gameplay then the only way for those slaves to get a reference the master actor is for the slaves to scan through all the actors on the level when created ( i.e. run a foreach.allactors iteration in prebeginplay ) and identify the master actor (hopefully it will be the only one of it's type on the level or we're in trouble).
So, to put things in more concrete terms, imagine you have a mod where a reactor goes critical and will blow once the opposing team has pulled enough control rods out (meanwhile the defending team can push them back in).
Each player on has a small device (we'll call it an RCM or Reactor Core Monitor) which can display a reactor temperature which is going to be varying according to how many rods are in or out, this means that there's
no simple timer here, the state of the reactor depends on the actions of the players and this info has to come from the reactor
itself.
One way of doing this would be for the reactor object to scan all actors on the level each tick and each time it finds an RCM it would call an UpdateStatus function, like this:
var float temperature; event tick( float deltatime) { local RCM monitor; modelheat(deltatime); // do calcs to see how hot we are if(temperature > critical_temperature) boom() else foreach allactors( class'RCM', monitor) monitor.UpdateStatus(temperature); }
Which is all well and good but somewhat inefficient as the foreach allactors iterator gets called every tick, this is slow and generally a Bad Thing.
Another way to approach this is to have the RCM's find out where the reactor is when they're created and then use that information to query the reactor themselves, like this:
var myreactor reactor; function prebeginplay () { // find the reactor and assign a pointer to it foreach allactors(class'myreactor', reactor); } event tick (float deltatime) { if(reactor !=none) UpdateStatus(reactor.temperature); }
The nasty slow iterator gets called only once per RCM actor (it'd probably be spawned as part of the players inventory on game login and that's when it scans to get a reference to the reactor) and all the reactor has to do is check how hot it's getting at regular intervals and set that value in it's temperature variable for the others to read... easy!
(and that mod ideas sounds like it might be fun... feel free to pinch it )