WIP 04 | Creating a MotionMap
Goal: Learn how to create a MotionWeb for a Crane using the MotionMap tool
Video Tutorial: Prespective How To: DES Intermediate Tutorial - Actor Motion Path Graph
Tip: Watch the video on YouTube in FullScreen mode and use HD quality (settings) to read the script clearly!
Introduction
This tutorial explains how you can create an MotionMap to control a crane where the Horizontal, Vertical and Rotator actors are dependent on each other. This MotionMap will be used in the next tutorial called “CranePickupAndDropoff” (https://unit040.atlassian.net/wiki/spaces/PAD/pages/2099216385)
Scene
The scene hierarchy consists of the following components:
DES Controller: Controls the simulation and therefore has to be at the top of the hierarchy
CraneInstructor: Controls the three crane actors according to the sequence in the CraneInstructor script and is of type MotionWebInstructor.
CraneRotatorActor: The top part of the crane that will rotate (vertical tower and horizontal boom) and that sits on the base. It has two children, the CraneVerticalActor and CraneHorizontalActor and is of type MotionWebActor.
CraneVerticalActor: The fixed horizontal boom with its child the CraneHorizontalActor. This child is the moving telescopic horizontal boom. Is of type MotionWebActor.
CraneHorizontalActor: The moving telescopic horizontal boom with its child the VacuumGripper. Is of type MotionWebActor.
MotionWebHorizontal: Spline web with cues that the CraneHorizontalActor can move over. Created by the ActorMotionPathGraph.
MotionWebVertical: Spline web with cues that the CraneVerticalActor can move over. Created by the ActorMotionPathGraph.
MotionWebRotator: Spline web with cues that the CraneRotatorActor can move over. Created by the ActorMotionPathGraph.
Setup steps
Create a new Scene.
Drag the DESControllerUnrigged prefab into the scene and unpack it.
Ensure the scene hierarchy looks as follows (DESController>CraneInstructor>CraneRotatorActor>CraneVerticalActor>CraneHorizontalActor):
Add a DESController script to the DESController GameObject, and similarly add the scripts in the Scripts folder to the correspondingly named GameObjects.
While still in the Scripts folder, right-click in the Asset Explorer. This will open a menu.
Navigate to Create > DES > New Actor Motion Path Graph and click on it to create a new MotionMap
With the new MotionMap selected, click on Open Motion Path Graph Editor within the Inspector. This will open a new window.
In this window, right click in the empty space. This will open the following menu:
Click on New > Node > Actor Node
Click on the newly created Node to select it. It should look like this:
Drag one of the Actors in the hierarchy to the Actor Transform field.
Enter a name for the motion web in the Motion Web Name field.
Repeat this for the other 2 remaining Actors.
The result should look like this:
Connect the Actors according to their hierarchy by click and dragging between Child and Parent ports:
Open the creation menu again by right clicking in the empty space, and select New > Node> Path Node. Repeat this twice so you have 3 new Path Nodes.
Connect each path to one of the actors using the Paths port on the Actor Nodes and the Owner Actor ports on the Path Nodes. The result should look like this:
Select the Path Node connected to the CraneHorizontalActor.
Enter a name in the Spline Name field.
Ensure Spline Type is set to STRAIGHT and Path type set to TRAVERSAL_PATH.
Ensure there are 2 shots in the Shots container.
Unfold both shots and rename them to HorizontalIn and HorizontalOut. If not named correctly, the simulation will not work!
Set the Type to LINKED_CUE for both shots.
In the Linked Instructor field, select the CraneInstructor for both shots.
With the HorizontalActor in its default position (as it was in the prefab), click on Record Shot for the HorizontalIn shot. See below for the default position:
Move the HorizontalActor to its extended position and click on Record Shot for the HorizontalOut shot. See below:
You can check if the shots were correctly recorded by clicking on Move Actor to Shot on either shot.
Select the CraneHorizontalActor Node in the MotionMap.
Click on Generate Motion Web.
A Spline with two Cues should now be generated in the scene:
Select the Path Node connected to the CraneVerticalActor.
Enter a name in the Spline Name field.
Ensure Spline Type is set to STRAIGHT and Path type set to TRAVERSAL_PATH.
Ensure there are 2 shots in the Shots container.
Unfold both shots and rename them to VerticalTop and VerticalBottom. If not named correctly, the simulation will not work!
Set the Type to LINKED_CUE for both shots.
In the Linked Instructor field, select the CraneInstructor for both shots.
With the VerticalActor in its default position (as it was in the prefab), click on Record Shot for the VerticalTop shot.
Move the VerticalActor to its bottom position and click on Record Shot for the VerticalBottom shot. See below:
You can check if the shots were correctly recorded by clicking on Move Actor to Shot on either shot.
Select the CraneVerticalActor Node in the MotionMap.
Click on Generate Motion Web.
A Spline with two Cues should now be generated in the scene:
Select the Path Node connected to the CraneRotatorActor.
Enter a name in the Spline Name field.
Ensure Spline Type is set to UNIT_CIRCLE and Path type set to LOOK_AT_PATH.
Ensure there are 2 shots in the Shots container.
Click on Add Shot in the Node and ensure there are now 3 shots.
Unfold all shots and rename them to RotatePickup, RotateMid and RotateDropoff. If not named correctly, the simulation will not work!
Set the Type to LINKED_CUE for both shots.
In the Linked Instructor field, select the CraneInstructor for both shots.
With the RotatorActor in its default position (as it was in the prefab), click on Record Shot for the RotatePickup shot.
Rotate the RotatorActor to its middle position (set the Y-rotation of the transform to 90) and click on Record Shot for the RotateMid shot.
Rotate the RotatorActor to its end position (set the Y-rotation of the transform to 180) and click on Record Shot for the RotateDropoff shot.
You can check if the shots were correctly recorded by clicking on Move Actor to Shot on either shot.
Select the CraneVerticalActor Node in the MotionMap.
Click on Generate Motion Web.
A Circular Spline with 3 Cues should now be generated in the scene:
Save the MotionMap by clicking on Save Motion Map in the top left corner of the window.
Select the CraneInstructor in the Scene Hierarchy.
Click on Re-Index Motion Web From Selection.
Select the CraneRotatorActor in the Scene Hierarchy.
Set Motion Mode on Motion Web to Absolute.
Set Motion Type on Motion Web to Look Rotation (and ensure that any other type is NOT selected!).
Ensure that Teleport Actor to Closest Node is NOT selected.
Press Play.
MotionMap
The following MotionMap was created in this tutorial:
Scripts
CraneInstructor Script
Script 1 The CraneInstructor script that controls the actors in the MotionWeb
using System.Collections.Generic;
using u040.prespective.prescripted.des;
using u040.prespective.prescripted.des.events;
using u040.prespective.prescripted.des.motionweb;
using UnityEngine;
namespace u040.prespective.demos.actorMotionPathGraph
{
//Define as MotionWebInstructor to use the ActorMotionPathGraph
public class CraneInstructor : MotionWebInstructor
{
//Used to start the sequence only once
bool started = false;
//Function that runs every frame
public override void OnSimulationFrameUpdate(double _passedFrameTime, double _totalFrameTime, long _frameID, int _framePass, ref List<DESEvent> _resultingEvents, ref List<DESRecord> _resultRecords)
{
//Exit if simulator is not running or sequence has already started
if (_framePass <= 0 || started)
{
return;
}
//Make sure sequence is only started once
started = true;
//Look up MotionWebActors in the scene
CraneRotatorActor _rotator = this.GetMotionWebActorsByType<CraneRotatorActor>()[0];
CraneVerticalActor _vertical = this.GetMotionWebActorsByType<CraneVerticalActor>()[0];
CraneHorizontalActor _horizontal = this.GetMotionWebActorsByType<CraneHorizontalActor>()[0];
Debug.Log("START STATEMACHINE");
//Statemachine Sequence
MotionWebInstructionSequence _sequence = new MotionWebInstructionSequence()
{
SequenceID = "CraneSequence",
OnSequenceComplete = null,
Verbose = false,
InstructionSteps = new List<MotionWebInstructionStep>()
{
//1: To VerticalTop
MotionWebInstructionStep.GetStepWithMotionVelocity(
"1_VerticalTop", //Name
.1d, //Velocity
_vertical, //Actor
"CUE_VerticalTop" //Target cue
),
//2: To HorizontalIn
MotionWebInstructionStep.GetStepWithMotionVelocity(
"2_HorizontalIn", //Name
.1d, //Velocity
_horizontal, //Actor
"CUE_HorizontalIn", //Target cue
new List<string>(){ "1_VerticalTop" } //Previous step
),
//3: To RotationPickup
MotionWebInstructionStep.GetStepWithMotionVelocity(
"3_RotateToPickup", //Name
.5d, //Velocity
_rotator, //Actor
"CUE_RotatePickup", //Target cue
new List<string>(){ "2_HorizontalIn" } //Previous step
),
//4: To HorizontalOut
MotionWebInstructionStep.GetStepWithMotionVelocity(
"4_HorizontalOut", //Name
.1d, //Velocity
_horizontal, //Actor
"CUE_HorizontalOut", //Target cue
new List<string>(){ "3_RotateToPickup" } //Previous step
),
//5: To VerticalBottom (PICKUP)
MotionWebInstructionStep.GetStepWithMotionVelocity(
"5_VerticalBottom", //Name
.1d, //Velocity
_vertical, //Actor
"CUE_VerticalBottom", //Target cue
new List<string>(){ "4_HorizontalOut" } //Previous step
),
//6: To VerticalTop
MotionWebInstructionStep.GetStepWithMotionVelocity(
"6_VerticalTop", //Name
.1d, //Velocity
_vertical, //Actor
"CUE_VerticalTop", //Target cue
new List<string>(){ "5_VerticalBottom" } //Previous step
),
//7: To HorizontalIn
MotionWebInstructionStep.GetStepWithMotionVelocity(
"7_HorizontalIn", //Name
.1d, //Velocity
_horizontal, //Actor
"CUE_HorizontalIn", //Target cue
new List<string>(){ "6_VerticalTop" } //Previous step
),
//8: To RotationDropoff
MotionWebInstructionStep.GetStepWithMotionVelocity(
"8_RotateToDropoff", //Name
.5d, //Velocity
_rotator, //Actor
"CUE_RotateDropoff", //Target cue (one exit)
new List<string>(){ "7_HorizontalIn" } //Previous step
),
//9: To HorizontalOut
MotionWebInstructionStep.GetStepWithMotionVelocity(
"9_HorizontalOut", //Name
.1d, //Velocity
_horizontal, //Actor
"CUE_HorizontalOut", //Target cue
new List<string>(){ "8_RotateToDropoff" } //Previous step
),
//10: To VerticalBottom (DROPOFF)
MotionWebInstructionStep.GetStepWithMotionVelocity(
"10_VerticalBottom", //Name
.1d, //Velocity
_vertical, //Actor
"CUE_VerticalBottom", //Target cue
new List<string>(){ "9_HorizontalOut" } //Previous step
),
//11: To VerticalTop
MotionWebInstructionStep.GetStepWithMotionVelocity(
"11_VerticalTop", //Name
.1d, //Velocity
_vertical, //Actor
"CUE_VerticalTop", //Target cue
new List<string>(){ "10_VerticalBottom" } //Previous step
),
//12: To HorizontalIn
MotionWebInstructionStep.GetStepWithMotionVelocity(
"12_HorizontalIn", //Name
.1d, //Velocity
_horizontal, //Actor
"CUE_HorizontalIn", //Target cue
new List<string>(){ "11_VerticalTop" } //Previous step
),
//13: To RotationPickup
MotionWebInstructionStep.GetStepWithMotionVelocity(
"13_RotateToPickup", //Name
.5d, //Velocity
_rotator, //Actor
"CUE_RotatePickup", //Target cue
new List<string>(){ "12_HorizontalIn" } //Previous step
)
} //InstructionSteps
}; //Sequence
//Register the sequence
InstructionSequences.Add(_sequence);
//Start the sequence
_sequence.StartSequence(this, _passedFrameTime);
//NOT NEEDED
//base.OnSimulationFrameUpdate(_passedFrameTime, _totalFrameTime, _frameID, _framePass, ref _resultingEvents, ref _resultRecords);
} //OnSimulationFrameUpdate
} //class
} //namespace
CraneRotatorActor Script
Script 2 The CraneRotatorActor script that defines this actor to be a MotionWebActor so it can move around in the MotionWeb
using u040.prespective.prescripted.des.motionweb;
namespace u040.prespective.demos.actorMotionPathGraph
{
//Must be of MotionWebActor class to be used in the ActorMotionPathGraph
public class CraneRotatorActor : MotionWebActor
{
}
}
CraneVerticalActor Script
Script 3 The CraneVerticalActor script that defines this actor to be a MotionWebActor so it can move around in the MotionWeb
using u040.prespective.prescripted.des.motionweb;
namespace u040.prespective.demos.actorMotionPathGraph
{
//Must be of MotionWebActor class to be used in the ActorMotionPathGraph
public class CraneVerticalActor : MotionWebActor
{
}
}
CraneHorizontalActor Script
Script 4 The CraneHorizontalActor script that defines this actor to be a MotionWebActor so it can move around in the MotionWeb
Playmode
Watch the video above to see what happens when we press play. The CraneInstructor starts moving the three crane actors according the statemachine sequence and stops at the end of the sequence.
Prespective Documentation