In addition to the "turning to follow" behavior defined in Agent 1, Agent 2 assess its distance from a target and if it judges that it is not "too close", then it will strafe (walk or run) towards that target.
Agent 2 introduces the following concepts:
In the UML Chart on the right, we'll want to create a new Use Case for our
new behavior.
Our new behavior seems to comprise of three sub-steps
Q: Why are the connections between Assess Range, Find Outside Range and Strafe to Target dashed instead of solid?
Agent2 retains the same top-level control structure as Agent1 -
This control structure becomes a bit more complicated, as Agent2 will have
multiple paths of behavior that will execute in parallel and will have to yield
to each other.
As a result, we introduce a number of new pieces of control
structure:
Since we would like Agent2 to Strafe to target and Rotate to follow agent, we know that the state chart should have two branches of execution, and hence, a fork.
Now we would like a state diagram that does the following:
So 3 definite states have been described here -
Clearly, we would like these states to occur in the order shown above, but we must somehow account for the "rotate/follow" branch. If state activation in our new branch continues beyond "outside-range", then we would like to supress the "rotate/follow" branch. We'll do this by utilizing subsumption through a couple of threshold devices. These are represented in the UML as follows:
Now we have all the important states, but remember that the framework will step through each branch in a breadth first fashion. In other words, the framework will execute one step of rotate/follow, then one step of assess-range, then one more step of rotate/follow, and another of assess range. So as your state chart is now, anytime the agent decides it is outside of range and should *not* rotate/follow, it has already rotate/followed...not so good.
So...to fix this problem, we insert relays to be sure that each branch has an equal amount of states. A simple relay is a "decision" element with an "sr" stereotype.
Your completed state chart should now look something like this:
Each State we've just created - assess-range, outside-range and strafeto-target - each need rules for passing on activation. Recall how we created the state chart diagrames for the rotate/follow and turnto-location rules:
We'll walk through one example together, and then it will be up to you to
finish the rest. We'll start with the rule for 2-outside-range:
IF
target is found to NOT be within range, THEN assert an intention to strafe
towards the target.
(=> (and (within-range ?agent ?target no)
(location ?target ?x ?y ?z))
(strafeto-target ?x ?y ?z 1 ?target))
Now we'll create the statechart for this rule:
The completed rule for outside-range should look like this:
Please complete the rules for strafeto-target and assess-range in a similar fashion - feel free to consult the Agent 1 Tutorial for additional guidance. The rules are given below:
2-assess-range:
IF agent is > 200 game units from a target,
THEN assert that agent is NOT within range, ELSE assert that agent IS within
range.
(=> (and (target ?target ?agent)
(location ?target ?tx ?ty ?tz)
(location ?agent ?ax ?ay ?az)
(func within-n-units? ?ax ?ay ?tx ?ty 200 ?result))
(within-range ?agent ?target ?result)
For 2-strafeto-target: (=> (strafeto-target ?x ?y ?z ?priority ?target)
(upload-cmd strafeto-target))
Notice that the rule for assess-range contains the following (func within-n-units? ?ax ?ay ?tx ?ty 200 ?result)
This is a lisp function call - given the arguments ax ay tx ty and n - it places the return value in ?result. Because the actual definition of functions is not really integral to the UML description, we place these in a seperate file. As a result, in the UML we can treat functions the same as predicates, nothing special must be done.
Once you have completed the Agent2 specification, save your changes, go to lisp and run the following (don't forget to (mk:compile-system :ut-bots))
If your UML is correct, this command will complete without errors, otherwise, you missed something and will have to dig through the debugger to try and figure out what it was, which doesn't exactly offer many useful suggestions. Most likely you forgot a stereotype or used an invalid lisp expression for a name somewhere. Opening up the file to which you directed the generated code can also give you some clues.
Once you have generated code successfully, you can load it (File > Load).
Remember that we used a function in one of our rules, so we must define it.
(defun within-n-units? (ax ay tx ty n)
(if (<= (vec-length (vector (- ax tx) (- ay ty))) n)
'yes
'no))
You can put this in a seperate file and load that if you get sick of typing it in.
Now to insert your agent into UT...make sure UT server is running, and use (manage-agents) to get things started.