WIP 02 | Responding To Actor Motion Using Cues
Goal: Learn how an Actor interacts with a Cue via an Instructor.
Video Tutorial: Prespective How To: DES Basics - Responding To Actor Motion Using Cues
This tutorial shows how to detect an Actor passing a Cue using an Instructor.
Cues are used in the scene to trigger actions. In order to function, Cues are always required to be part of a Spline. They are used to communicate with Instructors to trigger behavior. A Cue can be located anywhere over the entire length of the Spline. Furthermore, the Cue has a “thickness” area to it that defines the size of the trigger area of the Cue on the Spline. Actors are detected when passing through the Cue's area. By increasing the Cue’s thickness, the trigger area increases. To trigger an action, a Cue must be assigned to an Instructor. That Instructor uses the Cue's information to call actions and events.
Two Cues can be found in the tutorial scene. The first Cue (DetectActorMovingPastArea) has a thickness, creating an interaction area. A second Cue (DetectActorMovingPastPoint) does not have a thickness, representing a single point on the Spline. Both Cues are assigned to the same Instructor InstructorRespondingToActorMovingPast. Within the Instructor’s inspector, we can see that both Cues are assigned to this Instructor by unfolding the Cue's foldout.
To understand what happens within the Instructor, we will take a look at its code.
InstructorRespondingToActorMovingPast script
#if PRESPECTIVE_SOURCE || ALPHA
using u040.prespective.prescripted.des.activities.motiontensor;
using u040.prespective.prescripted.des.instructors;
using u040.prespective.prescripted.des.participant.actor;
using u040.prespective.prescripted.des.participant.cue;
using UnityEngine;
namespace u040.prespective.tutorials.desbasics
{
/// <summary>
/// Demo script to show how an Instructor can respond to events related to an actors' motion, leading to interactions with Cues
/// </summary>
public class InstructorRespondingToActorMovingPast : DESInstructor
{
/// <summary>
/// When an Instructor has Cues linked to it, all intersection events with these Cues are sent to this method.
/// The intersecting Actor can then receive instruction from this Instructor as a result of that intersection.
/// This Instructor logs information about all its Cues' intersections.
/// </summary>
/// <param name="_actor">The intersecting Actor</param>
/// <param name="_cue">The intersecting Cue</param>
/// <param name="_intersectionEvent">Detailed information about the intersection</param>
/// <returns>Whether this Instructors' response was breaking, thus requiring the simulation to recalculate behavior in this simulation frame from this point in time in the simulation</returns>
public override bool ApplyInstruction(ADESActor _actor, ADESCue _cue, ActorCueIntersectionEvent _intersectionEvent)
{
// To Determine the total simulation time add the event time (which is the passed time in this simulation frame) to the TotalSimTimePassed (which is the total simulation time up to the start of this frame)
double totalSimulationTime = SimulationController.TotalSimTimePassed + _intersectionEvent.EventTime;
// Setup a message on who made this Cue intersection
string eventString = "Instructor " + this.name + " received an intersection event. The Actor " + _actor.name + " passed the Cue " + _cue.name + " at simulated time of " + totalSimulationTime + " seconds.\n";
eventString += "It intersected with this Cue with its";
// The Intersection Type in the eventdata tells us what parts of the Actor and this Cue actually interacted
switch (_intersectionEvent.IntersectionType)
{
case ActorCueIntersectionEvent.CueIntersectionType.Enter:
eventString += " front and thus entered the Cue area.\n";
break;
case ActorCueIntersectionEvent.CueIntersectionType.ForcedEnter:
eventString += " front and thus entered the Cue area, but it did this without conforming to deterministic rules (i.e. it was likely spawned or teleported here).\n";
break;
case ActorCueIntersectionEvent.CueIntersectionType.Center:
eventString += " center point, against the Cues center point.\n";
break;
case ActorCueIntersectionEvent.CueIntersectionType.Stay:
eventString += " body, this was reported in a previous frame, this is just a repeat message (note that this type of event is only invoked when enabled on the Cue).\n";
break;
case ActorCueIntersectionEvent.CueIntersectionType.Exit:
eventString += " rear and thus left the Cue area.\n";
break;
case ActorCueIntersectionEvent.CueIntersectionType.ForcedExit:
eventString += " rear and thus left the Cue area, but it did this without conforming to deterministic rules (i.e. it was likely destroyed or teleported away from here).\n";
break;
}
eventString += "The Actor was moving at " + _intersectionEvent.ActorVelocity + "m/s over the Spline " + _cue.OwnerSpline.name + " at the time of this event. ";
// Finally, log the event text
Debug.Log(eventString);
// Since the intersections this Actor makes with Cues do not change anything in the simulation (we only output stuff to the console), we do not need to make this a breaking event
return false;
}
}
}
#endif
The Instructor script consists of the following parts:
public class InstructorRespondingToActorMovingPast : DESInstructor
This Instructor is derived from the DESInstructor base class.
public override bool ApplyInstruction(ADESActor _actor, ADESCue _cue, ActorCueIntersectionEvent _intersectionEvent)
The ApplyInstruction is the main method and it overrides and adds its own instructions to the ApplyInstruction in the base class. When an Instructor has Cues assigned to it, all intersection events with these Cues are sent to this method. The method is called ApplyInstruction because the Instructor is in control of a subsystem, whereas Actors moving over Splines are mostly unaware of their role and simply respond to instructions.
The three input parameters required: the Actor triggering the intersection event, the Cue that is intersected with and the type of intersection that occurred. Using this information, we can proceed within our method.
There are three main types of intersection events that can be triggered. The first is when an Actor enters the Cue area. The second is when the Actor crosses the center point of the Cue. And the third, when the Actor leaves the Cue area. A fourth type is stay, which is triggered when an Actor remains within the Cue area, but this event is only invoked when manually enabled on the Cue.
There are also two Forced events, which occur when the deterministic rules were not confirmed. It was likely that the Actor was forced in or out of the Cue, for example due to spawning Actors.
Enter: Actor enters the Cue area
Center: Actor crosses the center of the Cue
Exit: Actor leaves the Cue area
Stay: Actor stays in the Cue area
Forced Enter: Actor is forced into the Cue area (e.g. by spawning)
Forced Exit: Actor is forced out of the Cue area
The current simulation time is calculated by adding the time of the intersection event to the total time that has passed.
The string eventString is used to collect information about the intersection event and display it at the end of the method. Value this.name
refers to the name of this Instructor, because this refers to the object containing this script. Value _actor.name
refers to the name of the Actor triggering the Cue. Value _cue.name
refers to the name of the Cue that is triggered
The Switch method determines what kind of intersection event is triggered based on the triggered IntersectionType (Enter, Exit etc.). A related message is added to the _eventString
that will be printed later. Each case ends in a break, because only one intersection event triggered the method.
Extra info is added and logged to the console message.
The ApplyInstruction needs to return a boolean dependent on whether a breaking event happened or not. A breaking event triggers a recalculation and is required when components have been altered in the simulation due to the method. Since we only output text in the console and did not affect the simulation, we do not need a breaking event and can return false.
Breaking events: A breaking event is any event during the simulation that changes the course of the simulation. For example, if an Actor has its velocity changed mid-frame, its position at the end of the frame is different from when it would not have its velocity changed. This forces the simulation to recalculate the Actor’s path from the point of the velocity change in order to find the correct position at the end of the frame.
Play Mode
When we press play, we will see that the Actor initiates its Motion Tensor and moves over the Spline. When it crosses the Cue with thickness, we see that it takes 0.2 seconds to cross the Cue. When it crosses the Cue without thickness, it takes 0.1 second to cross the Cue. All information is shown in the console.
Prespective Documentation