Create a custom marker
This workflow demonstrates how to create a custom marker that you can use to add notes to your Timeline instance. This workflow also demonstrates how to change the default appearance of a custom marker with scripting and a Unity Style Sheet (USS).
If you are unfamiliar with markers and signals, it is recommended that you follow the markers and signals workflow before this workflow.
The custom marker created in this workflow is a simplified version of the custom Annotation marker found in the Customization Samples sample. Consult Timeline Samples for information on the available Timeline samples and how to import them into your projects.
To demonstrate how to create a custom marker, this workflow is divided into the following main tasks:
- Set up the Timeline instance.
- Add folders for scripts and files.
- Create a Notes marker with custom properties.
- Add a line overlay.
- Customize the marker's appearance with USS.
- Add a marker overlay.
- Override the default tooltip.
- Check your scripts.
- Add more features.
Set up the Timeline instance
The examples in this workflow use the Timeline instance named NotesTL
which has a single Animation track bound to the DefaultMale
model. The content of this Animation track is the result of following the Animate a humanoid workflow.
The NotesTL Timeline instance before creating a custom marker
It is not necessary to create the same Timeline instance shown in this workflow. You can use any Timeline instance to follow this workflow.
Add folders for scripts and files
In a Unity project, you store scripts, images, and other supporting files in the Assets
folder. You can keep the Assets
folder organized by adding additional folders and sub-folders. To ensure that some scripts, images, and assets are recognized by the Unity Editor, some folders must have specific names, be placed in a specific folder structure, or both.
To organize the Assets
folder and create a recognized folder structure, follow these steps:
In the Project view, right-click the
Assets
folder and choose Create > Folder from the context menu.Name this new folder
Notes
. This folder will store the scripts, assets, and images for overriding the default marker behavior and appearance.In the
Notes
folder, add a new folder and name itEditor
. This folder will store a custom script that overrides the default marker behavior and creates a custom editor. This folder must be namedEditor
.In the
Editor
folder, add a new folder and name itStylesheets
.In the
Stylesheets
folder, add two new folders. Name one folderExtensions
and the otherResources
. TheExtensions
folder will store a Unity Style Sheet. This file must be in the pathEditor/Stylesheets/Extensions
to be recognized. TheResources
folder will store the image resource files used by the Unity Style Sheet. Resource images must be in the pathEditor/Stylesheets/Resources
to be recognized.Project window with the
Notes
folder and its paths expanded.
Create a Notes marker with custom properties
To create a custom Notes marker with custom properties, you must write a script with a class that inherits from the Marker
class. To do this, follow these steps:
In the Project window, right-click the
Notes
folder and choose Create > C# Script from the context menu. This adds aNewBehaviourScript
file to theNotes
folder.Rename the file
NotesMarker.cs
. For Unity to recognize this script, the file name must match the class name.Select the
NotesMarker
file in theNotes
folder and choose Open in the Inspector window. TheNotesMarker
script opens for editing in your External Script Editor.In your script editor, replace the contents of the
NotesMarker
file with the following:using System.ComponentModel; using UnityEngine; using UnityEngine.Timeline; public class NotesMarker : Marker { public string title = "empty"; public Color color = new Color(1.0f, 1.0f, 1.0f, 0.5f); public bool showLineOverlay = false; [TextArea(10, 15)] public string note; }
Save the script and switch to the Unity Editor. When you save a script and switch to the Unity Editor, it recompiles the script and checks for errors or warnings. If found, errors and warnings are written to the Console window. To view the Console window, select Window > General > Console.
When the script is corrected and recompiled, you can add the Notes marker to your Timeline instance.
For example, to add the custom Notes marker to the expanded Timeline Marker track, right-click on the Marker track and select Add Notes Marker from the context menu.
Notes marker added to the Marker track. The Notes marker has the same appearance as the default marker.
The added marker is selected and its custom properties display in the Inspector window. Modifying the custom properties does not affect the selected marker.
Except for the Time property, the Notes marker properties are not connected to the selected marker
For example, if you enable Show Line Overlay, a line overlay is not drawn. If you change the Color property, the color of the selected Notes marker does not change. More scripting is necessary before modifying a custom property in the Inspector window affects the selected Notes marker.
Add a line overlay
For the custom properties in the Inspector window to affect the selected Notes marker, you must write a script with a class that inherits from the MarkerEditor
class.
The MarkerEditor
class is similar to a custom Inspector. You override methods in the MarkerEditor
class to perform actions such as drawing a colored line overlay.
To do this, follow these steps:
In the Project window, right-click the
Editor
folder and choose Create > C# Script from the context menu.Name the script
NotesMarkerEditor.cs
.Select the
NotesMarkerEditor
file in theEditor
folder and choose Open in the Inspector window to open this file for editing.In the script editor, replace the contents of the
NotesMarkerEditor.cs
file with the following code snippet:using System; using UnityEditor; using UnityEditor.Timeline; using UnityEngine; using UnityEngine.Timeline; // Editor used by the Timeline window to customize the appearance of a NotesMarker [CustomTimelineEditor(typeof(NotesMarker))] public class NotesMarkerEditor : MarkerEditor { // Set a constant for the transparency of overlays const float k_OverlayAlpha = 0.5f; // Override this method to draw a vertical line over the Timeline window's contents. public override void DrawOverlay(IMarker marker, MarkerUIStates uiState, MarkerOverlayRegion region) { // Check if marker is not NotesMarker. Set notes as local variable. if (marker is not NotesMarker notes) { return; // If not, return without drawing an overlay } // If NotesMarker, check if Show Line Overlay property is true if (notes.showLineOverlay) { DrawLineOverlay(notes.color, region); // if Show Line Overlay is true, call function to draw vertical line } } static void DrawLineOverlay(Color color, MarkerOverlayRegion region) { // Calculate a rectangle that uses the full timeline region's height and marker width Rect overlayLineRect = new Rect(region.markerRegion.x, region.timelineRegion.y, region.markerRegion.width, region.timelineRegion.height); // Set the color with an extra alpha value adjustment, then draw the rectangle Color overlayLineColor = new Color(color.r, color.g, color.b, color.a * k_OverlayAlpha); EditorGUI.DrawRect(overlayLineRect, overlayLineColor); } }
In the
NotesMarkerEditor
script, the CustomTimelineEditor attribute associatesNotesMarker
with theNotesMarkerEditor
.The
NotesMarkerEditor
script overrides the DrawOverlay method. This overridden method checks the type of marker. If the marker is not aNotesMarker
, the method returns without drawing an overlay.If the marker is a
NotesMarker
and thenotes.showLineOverlay
property is true, theDrawOverlay
method calls theDrawLineOverlay
method. TheDrawLineOverlay
method draws a colored line overlay based on thenotes.color
property and MarkerOverlayRegion. This region includes two sub-regions that you can use to calculate the rectangle for the line overlay:
markerRegion
: The region of the marker. You can use this rectangle to draw directly on the marker itself.timelineRegion
: The region of the Content view in the Timeline window. The height of the Timeline window is derived fromregion.timelineRegion.y
.
Save the script, switch to the Unity Editor, and correct any errors or warnings. Errors and warnings are written to the Console window. To view the Console window, select Window > General > Console.
In the Timeline window, select the Notes marker on frame 30.
In the Inspector window, enable Show Line Overlay and modify the Color property to change the color overlay from white to red.
Notes marker at frame 30 is drawn with a red line overlay.
Customize the marker's appearance with USS
When you create a custom marker, it uses the same images as the default Signal Emitter marker. This makes it difficult to visually distinguish a custom marker from a default marker.
In this workflow, the Notes marker uses the same images as the default Signal Emitter marker. To change the appearance of the Notes marker, you can use a Unity Stylesheet (USS).
Before creating the USS file, you should have an image for each state of the Notes marker. This workflow uses three states: collapsed, expanded, and selected. The following table provides an image for each Notes marker state:
Image/State | Description |
---|---|
collapsed |
The notes_collapsed.png image displays when the Notes marker is collapsed. |
expanded |
The notes_expanded.png image displays when the Notes marker is expanded but not selected. |
selected |
The notes_selected.png image displays when the Notes marker is expanded and selected. |
To modify the Notes marker to use these images, follow these steps:
Copy the three images from the table above to the
Assets/Notes/Editor/Stylesheets/Resources
folder. These images must be located in theResources
folder to be recognized by the USS file.In the Project window, right-click the
Extensions
folder in theAssets/Notes/Editor/Stylesheets
path and choose Create > UI Toolkit > Style Sheet from the context menu. ANewUSSFile.uss
is created in theExtensions
folder.Rename the
NewUSSFile
file ascommon
. This USS file must be namedcommon.uss
and must be saved in theEditor/Stylesheets/Extensions
path for it to be recognized by Unity.Select the
common
USS file in theExtensions
folder and choose Open in the Inspector window. Thecommon.uss
file opens for editing in your External Script Editor.In the script editor, replace the contents of the
common.uss
file with the following:/* Custom USS stylesheet. */ /* NotesMarker uses this style and image when marker is collapsed.*/ .NotesStyle { width:16px; height:16px; background-image: resource("notes_collapsed"); } /* NotesMarker uses this style and image when the marker is selected.*/ .NotesStyle:hover:focus:checked { background-image: resource("notes_selected"); } /* NotesMarker uses this style and image when the marker is not selected.*/ .NotesStyle:checked { background-image: resource("notes_expanded"); }
USS styles support pseudo-states, which are similar to pseudo-classes in CSS. Timeline markers use pseudo-states to set its image depending on whether the marker is collapsed, expanded, or selected:
Collapsed: no pseudo-state
Expanded:
checked
pseudo-stateSelected: combination
hover:focus:checked
pseudo-statesThe
common.uss
file defines USS styles for both the light and dark Editor Theme.If you want to define specific styles for the Unity light and dark themes, in the same folder as the
common.uss
file, add thelight.uss
anddark.uss
style sheets. The styles defined in these USS files will override styles in thecommon.uss
file.
To associate the Notes marker with the custom
NotesStyle
style, add theCustomStyle
attribute to theNotesMaker
script. Add the attribute to the script before the class definition:... [CustomStyle("NotesStyle")] public class NotesMarker : Marker { ... }
Save the
NotesMarker
script, switch to the Unity Editor, and correct any errors or warnings. Errors and warnings are written to the Console window. To view the Console window, select Window > General > Console.When the script is corrected and recompiled, the appearance of each Notes marker changes depending on whether the marker is selected, not selected, or collapsed:
(A) The Notes marker at frame 30 is selected. (B) The Notes marker at frame 60 is not selected. (C) The Notes markers at frames 90 and 150, on the Animation track, are collapsed.
Add a marker overlay
At this point in the workflow, the Notes marker uses custom images and uses the Color property when the Show Line Overlay property is enabled. The Color property is not used when the Show Line Overlay is disabled.
To use the Color property to add color to the Notes marker in all states, expand the conditional statement for the Show Line Overlay property to include a draw marker overlay method. This method draws an overlay for the size of the Notes marker.
To do this, follow these steps:
Select the
NotesMarkerEditor
script in theAssets/Notes/Editor
folder and choose Open in the Inspector window. This script opens for editing in your External Script Editor.In the
NotesMarkerEditor
script, expand theif
statement to include anelse
statement which calls a new function namedDrawMarkerOverlay
:// If NotesMarker, check if Show Line Overlay is true if (notes.showLineOverlay) { DrawLineOverlay(notes.color, region); // if Show Line Overlay is true, draw vertical line } else // If NotesMarker and Show Line Overlay is false, draw marker overlay { DrawMarkerOverlay(notes.color, region, uiState); }
If the marker is a
NotesMarker
and thenotes.showLineOverlay
property is false, theDrawOverlay
method calls theDrawMarkerOverlay
function. TheDrawMarkerOverlay
function is similar toDrawLineOverlay
except an additional parameter is included to check the UI state of the Notes marker.In the
NotesMarkerEditor
script, after theDrawLineOverlay
function, add the newDrawMarkerOverlay
function:static void DrawMarkerOverlay(Color color, MarkerOverlayRegion region, MarkerUIStates state) { // By default, set the height to the markerRegion's height float markerHeight = region.markerRegion.height; // If marker is collapsed, set the height to 2/3 the markerRegion's height if (state.HasFlag(MarkerUIStates.Collapsed)) { markerHeight = region.markerRegion.height / 1.5f; } // Calculate a rectangle that uses the marker region and variable markerHeight Rect overlayMarkerRect = new Rect(region.markerRegion.x, region.markerRegion.y, region.markerRegion.width, markerHeight); Color overlayMarkerColor = new Color(color.r, color.g, color.b, color.a * k_OverlayAlpha); EditorGUI.DrawRect(overlayMarkerRect, overlayMarkerColor); }
The
DrawMarkerOverlay
function draws a colored rectangle based on thenotes.color
property and the MarkerOverlayRegion. The function uses themarkerRegion
region and the UI state to calculate the size of the overlay.For example, if the Notes marker is collapsed (
state.HasFlag(MarkerUIStates.Collapsed)
) then the rectangle is set to two-thirds the height of the marker region. This matches the height of thenotes_collapsed.png
file. When the Notes marker is not collapsed, the marker overlay uses the full height of the Notes marker.Save the
NotesMarkerEditor
script, switch to the Unity Editor, and correct any errors or warnings. Errors and warnings are written to the Console window. To view the Console window, select Window > General > Console.After the script is corrected and recompiled, each Notes marker is drawn using its Color property. For example, if
(A) The Notes marker at frame 30 is red. (B) The Notes marker at frame 60 is green. (C) The Notes markers at frames 90 and 150, on the Animation track, are collapsed and blue.
Override the default tooltip
A tooltip is a brief two or three sentence description that displays when you hover the cursor over an interface element such as a property name, a button, or an option.
If you hover the cursor over the default Timeline marker, the tooltip displays the name of its Signal asset. Because the custom Notes marker does not emit a signal, when you hover over the Notes marker, nothing displays.
You can override the MarkerDrawOptions
function in the NotesMarkerEditor
script to use the tooltip to display the title of the Notes marker. To do this, follow these steps:
In the Project window, select the
NotesMarkerEditor
script in theAssets/Notes/Editor
folder and choose Open in the Inspector window. The script opens for editing in your External Script Editor.Override the
GetMarkerOptions
method by inserting the following code snippet after theconst float k_OverlayAlpha = 0.5f
assignment:// Use MarkerDrawOptions to override the marker's tooltip if the marker is of type NotesMarker. public override MarkerDrawOptions GetMarkerOptions(IMarker marker) { // Check if marker is not NotesMarker and assign it to notes if (marker is not NotesMarker notes) { return base.GetMarkerOptions(marker); // If not, return with no tooltip override } return new MarkerDrawOptions { tooltip = notes.title }; // If NotesMarker, replace tooltip with contents of notes.title }
The GetMarkerOptions method provides information about the marker through the MarkerEditor class.
Save the
NotesMarkerEditor
script, switch to the Unity Editor, and correct any errors or warnings. Errors and warnings are written to the Console window. To view the Console window, select Window > General > Console.After the script is corrected and recompiled, when you hover over a Notes marker, the title of the Notes marker displays as a tooltip. By default, each Notes marker tooltip displays "empty". You can change the default title in the
NotesMarker
script. The tooltip also displays for collapsed Notes markers.
Check your scripts
This topic provides the complete scripts that you created and modified during this workflow. Use these listings to ensure that you have entered and edited the following scripts correctly:
Use these listings to ensure that your
NotesMarker
script
The complete NotesMarker.
cs script:
using System.ComponentModel;
using UnityEngine;
using UnityEngine.Timeline;
[CustomStyle("NotesStyle")]
public class NotesMarker : Marker
{
public string title = "empty";
public Color color = new Color(1.0f, 1.0f, 1.0f, 0.5f);
public bool showLineOverlay = false;
[TextArea(10, 15)] public string note;
}
NotesMarkerEditor
script
The complete NotesMarkerEditor.cs
file:
using System;
using UnityEditor;
using UnityEditor.Timeline;
using UnityEngine;
using UnityEngine.Timeline;
// Editor used by the Timeline window to customize the appearance of a NotesMarker
[CustomTimelineEditor(typeof(NotesMarker))]
public class NotesMarkerEditor : MarkerEditor
{
// Set a constant for the transparency of overlays
const float k_OverlayAlpha = 0.5f;
// Use MarkerDrawOptions to override the marker's tooltip if the marker is of type NotesMarker.
public override MarkerDrawOptions GetMarkerOptions(IMarker marker)
{
// Check if marker is not NotesMarker and assign it to notes
if (marker is not NotesMarker notes)
{
return base.GetMarkerOptions(marker); // If not, return with no tooltip override
}
return new MarkerDrawOptions { tooltip = notes.title }; // If NotesMarker, replace tooltip with contents of notes.title
}
// Override this method to draw a vertical line over the Timeline window's contents.
public override void DrawOverlay(IMarker marker, MarkerUIStates uiState, MarkerOverlayRegion region)
{
// Check if marker is not NotesMarker. Set notes as local variable.
if (marker is not NotesMarker notes)
{
return; // If not, return without drawing an overlay
}
// If NotesMarker, check if Show Line Overlay property is true
if (notes.showLineOverlay)
{
DrawLineOverlay(notes.color, region); // if Show Line Overlay is true, call function to draw vertical line
}
else // If NotesMarker and Show Line Overlay is false, draw marker overlay
{
DrawMarkerOverlay(notes.color, region, uiState);
}
}
static void DrawLineOverlay(Color color, MarkerOverlayRegion region)
{
// Calculate a rectangle that uses the full timeline region's height and marker width
Rect overlayLineRect = new Rect(region.markerRegion.x,
region.timelineRegion.y,
region.markerRegion.width,
region.timelineRegion.height);
// Set the color with an extra alpha value adjustment, then draw the rectangle
Color overlayLineColor = new Color(color.r, color.g, color.b, color.a * k_OverlayAlpha);
EditorGUI.DrawRect(overlayLineRect, overlayLineColor);
}
static void DrawMarkerOverlay(Color color, MarkerOverlayRegion region, MarkerUIStates state)
{
// By default, set the height to the markerRegion's height
float markerHeight = region.markerRegion.height;
// If marker is collapsed, set the height to 2/3 the markerRegion's height
if (state.HasFlag(MarkerUIStates.Collapsed))
{
markerHeight = region.markerRegion.height / 1.5f;
}
// Calculate a rectangle that uses the marker region and variable markerHeight
Rect overlayMarkerRect = new Rect(region.markerRegion.x,
region.markerRegion.y,
region.markerRegion.width,
markerHeight);
Color overlayMarkerColor = new Color(color.r, color.g, color.b, color.a * k_OverlayAlpha);
EditorGUI.DrawRect(overlayMarkerRect, overlayMarkerColor);
}
}
Add more features
The Notes marker is complete but you can continue adding features. Here are some suggestions:
- In the add a marker overlay step, you draw a colored overlay for the Notes marker. This changes the background color of the Notes marker but does not draw the image using color. To do this, you could load each marker image as a 2D texture and draw these images with the marker's color. Consult the custom Annotation marker sample for more on how to load 2D textures and draw them with a specific color.
- In the customize the marker's appearance with USS step, you use the
common.uss
file to define USS styles for both the light and dark Editor Theme. To define specific styles for the light and dark themes, you can create USS files namedlight.uss
anddark.uss
. These files, if present at the same path ascommon.uss
, overrides styles with the same name and pseudo-states.