playtest-unity/playtest/Library/PackageCache/com.unity.timeline@1.7.4/Documentation~/smpl_custom_annotation.md

8.8 KiB

Annotation marker sample

The Annotation sample provides a marker that can be used as a bookmark for your timeline.

Annotation

Here are the options available on an annotation:

Field Description
Title The annotation's title. This will be displayed as a tooltip, when hovering the mouse on the annotation.
Color The annotation's color in the Timeline window.
Show line overlay Use this option to show a vertical line that spans the full height of the Timeline window.

Custom marker workflow example

This example will demonstrate how to:

  • create a custom marker;
  • customize a marker with MarkerEditor;
  • use a custom USS style to draw a marker;
  • add additional commands with Actions;

1. Create an annotation marker

A marker is an item that can be added to a Timeline Asset and is used to represent a point in time. Markers also have a specialization, just like clips (Activation clip, Audio clip, Animation clip, etc).

In order to add a new type of marker, all we need to do is to create a class that inherits the Marker class:

public class AnnotationMarker : UnityEngine.Timeline.Marker {}

This custom marker can now be added to any track or on the timeline marker area:

New marker added to a timeline

We can add a title, description and color to the annotation:

public class AnnotationMarker : Marker
{
    public string title;
    public Color color;
    public string description;
    public bool showLineOverlay;
}

The annotation marker itself is now complete. But the customization work is not done yet. Timeline offers many customization abilities.

2. Customize the marker's appearance

A marker's appearance can be customized using a USS style or with MarkerEditor. Both paths have their advantages and drawbacks.

Custom USS style

A marker can use a USS style to specify its appearance. For more information on how to create custom USS styles, see how to define custom USS styles.

The CustomStyle attribute can be used to specify a style for a given marker:

[CustomStyle("AnnotationStyle")]
public class AnnotationMarker : Marker
{
    //...
}

AnnotationStyle is defined in a USS stylesheet and will be used when a marker is displayed on screen:

Custom style

USS styles are useful if the desired appearance is simple (i.e. when only using a texture icon). For more complex stuff (i.e. dynamically changing a marker's color), a MarkerEditor will be needed.

Custom editor

MarkerEditor can be used to augment the capabilities of a marker in the editor. It works like a custom Inspector; the CustomTimelineEditor attribute is used to tell Timeline that a MarkerEditor class should be associated to a given marker.

[CustomTimelineEditor(typeof(AnnotationMarker))]
public class AnnotationMarkerEditor : MarkerEditor
{
   //...
}

Marker information

MarkerEditor lets us provide information about the marker by overriding the GetMarkerOptions method.

public override MarkerDrawOptions GetMarkerOptions(IMarker marker)
{
   var annotation = marker as AnnotationMarker;
   if (annotation != null)
   {
      return new MarkerDrawOptions { tooltip = annotation.title };
   }
   return base.GetMarkerOptions(marker);
}

Here the tooltip of an Annotation has been set to use the annotation's title variable.

Marker tooltip

MarkerDrawOptions can also set the error text on a marker, which can be useful if a variable has been incorrectly set and needs attention.

Overlay

An overlay can be drawn on top of a marker by overriding the DrawOverlay method:

public override void DrawOverlay(IMarker marker, MarkerUIStates uiState, MarkerOverlayRegion region)
{
    var annotation = marker as AnnotationMarker;
    if (annotation != null)
    {
        //Draw overlay code...
    }
}

An overlay is drawn on top of the marker; the USS style is drawn first and DrawOverlay is called afterwards. For an Annotation, we can use DrawOverlay to change the color of the marker and to draw a line that spans the full Timeline window's height. To do this, we can use the information given in region. Along with the visible time range, MarkerOverlayRegion provides two rectangles that can be used to know where to draw:

  • markerRegion

markerRegion is the rectangle that encompasses the marker. This is useful to draw something directly on the marker itself. For Annotation, this rectangle is used to draw the color overlay.

marker region

  • timelineRegion

timelineRegion is the rectangle that encompasses the clips and markers region of the timeline window. This is useful to draw something out of the marker's region, like the Annotation's line overlay.

timeline region

const float k_LineOverlayWidth = 6.0f;
float markerRegionCenter = markerRegion.xMin + (markerRegion.width - k_LineOverlayWidth) / 2.0f;
Rect lineRect = new Rect(markerRegionCenter,
    timelineRegion.y,
    k_LineOverlayWidth,
    timelineRegion.height);

3. Create custom Actions

Timeline Action

Actions can be used to add new menu entries in Timeline's context menus. For an Annotation, we want to add a menu item available in all context menus to create an Annotation with the clipboard's contents. To do this, a TimelineAction is needed, along with the MenuEntry attribute.

[MenuEntry("Create Annotation from clipboard contents")]
public class CreateAnnotationFromClipboardContents : TimelineAction
{
    //...
}

MenuEntry lets Timeline know that this action can be added in context menus. Classes inheriting from TimelineAction need to override two methods: Execute and Validate.

Validate

Validate is used to specify that the action's prerequisites are fulfilled. In the case of CreateAnnotationFromClipboardContents, the action is only valid if there actually is contents in the clipboard. ActionValidity is used to describe the validity state of an action:

public override ActionValidity Validate(ActionContext context)
{
    if (!markers.All(marker => marker is AnnotationMarker))
        return ActionValidity.NotApplicable;

    string buffer = EditorGUIUtility.systemCopyBuffer;
    return buffer.Length == 0 ?  ActionValidity.Invalid : ActionValidity.Valid;
}
  • ActionValidity.Valid : The action can be executed.
  • ActionValidity.Invalid : The action cannot be executed given the current context and will appear grayed out in context menus.
  • ActionValidity.NotApplicable : The action does not apply to the current context and will not show up in menus.

Execute

Execute should run the code necessary to execute the action's purpose.

 public override bool Execute(ActionContext context)
{
    string buffer = EditorGUIUtility.systemCopyBuffer;
    TrackAsset track = context.tracks.FirstOrDefault();

    if (buffer.Length != 0)
    {
        // Create the new annotation and add it to the track
        //...
        return true;
    }

    return false;
}

The return value should specify if the execution succeeded or not.

Marker Action

It is also possible to write custom actions that apply only to markers, instead of all Timeline items. This is the purpose of the MarkerEditor class. It works just like TimelineAction, except that action applies to a list of markers.

A shortcut can also be assigned to an action. A static method with the TimelineShortcut attribute is needed. Invoker can be used to easily execute a given action:

[TimelineShortcut("Replace annotation description with clipboard", KeyCode.G)]
public static void InvokeAction()
{
    Invoker.InvokeWithSelectedMarkers<ReplaceAnnotationDescriptionAction>();
}

Notes

Runtime considerations

AnnotationMarker is available at runtime; it can be queried using, for example, TrackAsset.GetMarkers(). However, AnnotationMarkerEditor and custom actions are not available at runtime, since it depends on classes that are not part of the runtime assembly.