Document toolboxDocument toolbox

WIP 05 | Responding to the Event of an Actor reaching the end of a Spline

Goal: Learn how to create a Motion Tensor without using the Inspector and how to use Spline information to trigger events

Video Tutorial: https://www.youtube.com/watch?v=LrFL-fE2BnM

https://www.youtube.com/watch?v=LrFL-fE2BnM

When an Actor moves over a Spline , it is possible to track its process and use the information to enable actions to occur. In the scene we have a DESController, one ordinary Dspline without Cues and an Actor with a new script called FiniteSplineEventRespondingActor.

When we select the Actor, we see that our inspector is different compared to the previous tutorials. This Actor does not allow you to define a Motion Tensor manually, but you can have to define the Spline To Move Over. Set FiniteSpline as the Spline To Move Over. This Spline will be used in the script assigned to the Actor.

Inspector

Fig.1 Specify SplineToMoveOver in the Inspector

FiniteSplineEventRespondingActor script

The FiniteSplineEventRespondingActor script that creates a Motion Tensor on the Actor and detects when the Actor leaves the start of the Spline and reaches the end of the Spline.

#if PRESPECTIVE_SOURCE || ALPHA using System.Collections.Generic; using u040.prespective.core.spline; using u040.prespective.prescripted.des; using u040.prespective.prescripted.des.activities.motiontensor; using u040.prespective.prescripted.des.events; using u040.prespective.prescripted.des.participant.actor; using UnityEngine; namespace u040.prespective.tutorials.desbasics { /// <summary> /// Demo script to show how an Actor can create a Motion Tensor from code mid-simulation frame and can respond to events related to its traversal over the Spline (e.g. reaching the endpoint). /// </summary> public class FiniteSplineEventRespondingActor : DESActor { /// <summary> /// The Spline we'll use to move over /// </summary> public DSpline SplineToMoveOver; /// <summary> /// OnSimulationStart is run once at the start of the simulation. Any preparations necessary for the simulation can be done here. /// </summary> /// <param name="_resultingEvents">Container for the events that are calculated during start (they are invoked after the start on all simulated objects are completed)</param> /// <param name="_resultRecords">Container for the records that are created during start (they are processed for events after the start on all simulated objects are completed)</param> public override void OnSimulationStart(ref List<DESEvent> _resultingEvents, ref List<DESRecord> _resultRecords) { Debug.Log("Now creating a Motion Tensor with Spline endpoint events."); createMotionTensorInRuntimeFromCode(); // Run all inherrited code for this method base.OnSimulationStart(ref _resultingEvents, ref _resultRecords); } /// <summary> /// This function shows how to create a Motion Tensor from code mid-frame in a DES Simulation /// </summary> /// <param name="_frameTimePassed">The time passed in this simulation frame</param> private bool createMotionTensorInRuntimeFromCode() { // We havent set the Spline we want to move over in the inspector, so toss an error if (SplineToMoveOver == null) { Debug.LogError("Cannot Create Motion Tensor from code. The SplineToMoveOver was not set on GameObject " + this.gameObject.name + "."); return false; } // Prepare a Motion Tensor component. A component describes how this Actor will move over a specific Spline. In this case at a velocity of 1m/s with absolute translation and with a thickness. MotionTensorComponent newMotionTensorComponent = new MotionTensorComponent(SplineToMoveOver, MotionMode.Absolute, MotionType.Translation, .05d, 1d); // For this Motion Tensor component we need to tell on what types of events we want it to trigger a callback // If you want to respond on multiple event types split them using a '|' sign (and/or look at the documentation of the c# Flag type enum) // (e.g. the next line indicates that we want to get a callback on reaching something with both our centerpoint and our envelop, but we still want to automatically stop moving when we reach the end) newMotionTensorComponent.OnFiniteLimitCrossed = MotionTensorComponent.FiniteLimitResponse.CenterPointEvent | MotionTensorComponent.FiniteLimitResponse.EnvelopEvent | MotionTensorComponent.FiniteLimitResponse.CenterPointStop; // Generate the Motion Tensor Activity using the component description MotionTensor newMotionTensor = new MotionTensor(new List<MotionTensorComponent>() { newMotionTensorComponent }); // To be able detect we've reached an endpoint we need to register a listener on the LimitEventDelegate newMotionTensor.LimitEventDelegate = onComponentInTensorReachedLimit; // Make sure to set the Motion Tensor to running newMotionTensor.IsRunning = true; // And add the activity to this Actor (self) this.AddActivity(newMotionTensor); //Adding a Motion tensor should generally result in a breaking event, so return true return true; } /// <summary> /// This method is triggered when the Motion Tensor created in the 'CreateMotionTensorInRuntimeFromCode' method hits a limit on one of its components on a finite (non-closed) Spline /// </summary> /// <param name="_frameTimePassed">The time passed in this simulation frame</param> /// <param name="_totalFrameTime">The duration in seconds of a single simulation frame</param> /// <param name="_component">The component we're moving over</param> /// <param name="_limitEvent">Object containing information about the event</param> /// <returns>Whether this event should be considered a breaking event</returns> private bool onComponentInTensorReachedLimit(double _frameTimePassed, double _totalFrameTime, MotionTensorComponent _component, MotionTensorComponentLimitEvent _limitEvent) { // To get the total time since this simulation started add SimulationController.TotalSimTimePassed double totalSimulationTime = SimulationController.TotalSimTimePassed + _limitEvent.EventTime; Debug.Log("The Motion on Spline " + _component.Spline.name + " reached an event position: " + _limitEvent); // This Motion Tensor Component's centerpoint reached the 0% position on the Spline if (_limitEvent.CurveWithLimit == MotionTensorComponentLimitEvent.LimitedCurve.Center && _limitEvent.LimitEvent == MotionTensorComponentLimitEvent.LimitingEvent.LeavingStart) { Debug.Log("Actor " + this.name + " left the startpoint of the Spline with its centerpoint at a simulation time of " + totalSimulationTime + "s."); } // This Motion Tensor Component's front/back reach the 0% position on the Spline if (_limitEvent.CurveWithLimit == MotionTensorComponentLimitEvent.LimitedCurve.Envelop && _limitEvent.LimitEvent == MotionTensorComponentLimitEvent.LimitingEvent.LeavingStart) { Debug.Log("Actor " + this.name + " left the startpoint of the Spline with its Envelop at a simulation time of " + totalSimulationTime + "s."); } // This Motion Tensor Component's front/back reached the 100% position on the Spline if (_limitEvent.CurveWithLimit == MotionTensorComponentLimitEvent.LimitedCurve.Envelop && _limitEvent.LimitEvent == MotionTensorComponentLimitEvent.LimitingEvent.ArrivingAtEnd) { Debug.Log("Actor " + this.name + " reached The endpoint of the Spline with its Envelop at a simulation time of " + totalSimulationTime + "s."); } // This Motion Tensor Component's centerpoint reached the 100% position on the Spline if (_limitEvent.CurveWithLimit == MotionTensorComponentLimitEvent.LimitedCurve.Center && _limitEvent.LimitEvent == MotionTensorComponentLimitEvent.LimitingEvent.ArrivingAtEnd) { Debug.Log("Actor " + this.name + " reached The endpoint of the Spline with its centerpoint at a simulation time of " + totalSimulationTime + "s."); } //Since we're not changing anything, but only log information we can consider this event a non-breaking event return false; } } } #endif

 

 

public override void OnSimulationStart(ref List<DESEvent> _resultingEvents, ref List<DESRecord> _resultRecords) { Debug.Log("Now creating a Motion Tensor with Spline endpoint events."); createMotionTensorInRuntimeFromCode(); // Run all inherrited code for this method base.OnSimulationStart(ref _resultingEvents, ref _resultRecords); }

The OnSimulationStart method is executed before the simulation starts. Any preparation for the simulation you need to do, such as creating components from script, is best done here. In this case, the Motion Tensor for this Actor is generated here.

 

private bool createMotionTensorInRuntimeFromCode() { // We havent set the Spline we want to move over in the inspector, so toss an error if (SplineToMoveOver == null) { Debug.LogError("Cannot Create Motion Tensor from code. The SplineToMoveOver was not set on GameObject " + this.gameObject.name + "."); return false; } // Prepare a Motion Tensor component. A component describes how this Actor will move over a specific Spline. In this case at a velocity of 1m/s with absolute translation and with a thickness. MotionTensorComponent newMotionTensorComponent = new MotionTensorComponent(SplineToMoveOver, MotionMode.Absolute, MotionType.Translation, .05d, 1d); // For this Motion Tensor component we need to tell on what types of events we want it to trigger a callback // If you want to respond on multiple event types split them using a '|' sign (and/or look at the documentation of the c# Flag type enum) // (e.g. the next line indicates that we want to get a callback on reaching something with both our centerpoint and our envelop, but we still want to automatically stop moving when we reach the end) newMotionTensorComponent.OnFiniteLimitCrossed = MotionTensorComponent.FiniteLimitResponse.CenterPointEvent | MotionTensorComponent.FiniteLimitResponse.EnvelopEvent | MotionTensorComponent.FiniteLimitResponse.CenterPointStop; // Generate the Motion Tensor Activity using the component description MotionTensor newMotionTensor = new MotionTensor(new List<MotionTensorComponent>() { newMotionTensorComponent }); // To be able detect we've reached an endpoint we need to register a listener on the LimitEventDelegate newMotionTensor.LimitEventDelegate = onComponentInTensorReachedLimit; // Make sure to set the Motion Tensor to running newMotionTensor.IsRunning = true; // And add the activity to this Actor (self) this.AddActivity(newMotionTensor); //Adding a Motion tensor should generally result in a breaking event, so return true return true; }

The method createMotionTensorInRuntimeFromCode creates and adds a new Motion Tensor to the Actor.

The first thing it does is checking whether the SplineToMoveOver was set. If this is not be the case, no Motion Tensor can be created since it needs assigned to a specific Spline in order to function.

A Motion Tensor Component is part of the Motion Tensor. A Motion Tensor can have more than one Motion Tensor Components, each of which can describe motion over a Spline. The sum of all these components determine the resulting motion of an Actor.
The Motion Tensor is created with several parameters:

  • _spline: The Spline over which the Actor will move.

  • _motionMode: The Actor is set to have Absolute motion. This means that its motion will be over the absolute position on the Spline, rather than relative motion to the Spline.

  • _motionType: We set the type to Translation, which means that only positions will be set to the Actor’s Transform.

  • _thicknessInUnits: The thickness of the Actor. This will determine its size and thus the area it reserved on the Spline for itself, preventing other Actors from moving in that area.

  • _velocity: The velocity at which the Actor will move over its assigned Spline.

A Motion Tensor Component can trigger events when reaching Spline limits. In this case we want it to trigger events for reaching a Spline end with its center point or its back or front end (determined by its thickness), but also to stop moving when reaching that Spline end with its center point.

As soon as the Actor has reached a Spline end with its center point, its LimitEventDelegate is invoked. A listener for this event is added here, onComponentInTensorReachedLimit.

Lastly the Motion Tensor is set to running and added to the Actor. This is a breaking event and thus we return true.

 

 

The onComponentInTensorReachedLimit method was registered as a listener for limit events on the Actor. Its logs information on these events. Several if-statements test for which type of event was triggered. The property _limitEvent.CurveWithLimit tests for whether an event was tossed for the Actor's center point or its envelop. The property _limitEvent.LimitEvent tests for whether the Actor left the start of the Spline, or reached the end of the Spline.
This method returns false since no actual change was applied to the simulation, so this is not a breaking event.


Play Mode

When we press play, we will see the Actor move over the Spline and we see the logs of the listener displayed in the console. Aside from displaying logs, these events can be adapted to initiate events when the Actor leaves the start or reaches the end of the Spline.

 

Prespective Documentation