Target Indicators
Search Results for

    Show / Hide Table of Contents

    Target Indicator Manager

    The TargetIndicatorManager component is central to Target Indicators. It manages the lifecycle of all tracked targets, handling their addition, updates, and removal. This manager generates the screen-space data needed to position visual indicators.

    The following sections explain how to use the target indicator manager.

    Important

    By default Target Indicators does not display any UI on the scene. It only returns screen coordinates for you to display your own indicators. If you are interested in prebuilt visual indicators you can import the sample's starter assets which provides a solution with Unity UI. Refer to the Samples to learn more about how to configure the prebuilt indicators.

    Scene Setup

    Add the TargetIndicatorManager to a GameObject in the hiearchy and edit the configuration to your projects needs.

    The TargetIndicatorManager component attached to a GameObject.

    Configure the Target Indicator Manager

    The TargetIndicatorManager has several properties that can be edited within the inspector or set at runtime. The following sections explains each property in more detail.

    Camera

    The camera is used for calculating the screen space coordinates of each target. By default, when the TargetIndicatorManager component is added or reset it will assign the camera in your scene with the MainCamera tag. If no camera has the MainCamera tag it will fallback to using the first camera in the scene. Assign the camera that you will be using to display target indicators.

    To assign the camera reference at runtime, use TargetIndicatorManager.Camera.

    Boundary type

    The BoundaryType property controls how visual indicators are bounded. For example indicators can be bounded by the screen edges or have no boundary at all.

    There are four boundary types:

    1. Padded

      This boundary type clamps target indicators to the screen edges while applying an adjustable padding defined in pixels. Indicators automatically adapt to changes in screen size, maintaining their padding and visibility.

      An example of a padded rectangle boundary

      Padded boundary is common for indicating waypoints, enemies, and points of interest.

    2. Absolute

      This boundary type clamps target indicators to the screen with a fixed size defined in pixels. It will not change if the screen size changes.

      An example of an absolute ellipse boundary

      Absolute boundary is common in first person games where you want to indicate the direction a player or other character is attacking from when not directly in front of the user.

    3. Compass Tape

      This boundary type maps the direction of targets to a horizontal range, simulating a compass tape. Unlike other boundary types, it does not provide pixel-based screen coordinates. Instead, it offers a normalized value between 0 and 1 representing the target's cardinal direction relative to the user's forward direction.

      To learn more about how to use the screen pose for compass tape indicators, refer to Interpret the screen pose.

      An example of a compass tape boundary

    4. Unbounded

      This boundary type places visual indicators directly at the target's screen pose without any clamping. Consequently, if the target moves off-screen, its corresponding visual indicator will also move off-screen.

      An example of an unbounded tape boundary

    Boundary shape

    When using Padded or Absolute boundary types, configure the shape of the boundary with the BoundaryShape property.

    There are two boundary shapes:

    1. Rectangle

      An example of a padded rectangle boundary

      TargetIndicatorManager.Rectangle represents the rectangle data generated by the currnet configuration.

    2. Ellipse

      An example of a padded ellipse boundary

      TargetIndicatorManager.Ellipse represents the ellipse data generated by the current configuration.

    Padding

    When BoundaryType is set to Padded, you can define the pixel distance between the screen edges and the visual indicator's boundary.

    Padding can be independently configured for the Top, Bottom, Left, and Right edges. These properties are ignored if BoundaryType is not Padded.

    Width and Height

    When BoundaryType is set to Absolute, the boundary's dimensions are defined by the Width and Height properties.

    Unlike other boundary types, this size remains fixed and does not adapt to changes in screen size. These properties are ignored if BoundaryType is not Absolute.

    Visualize the boundary

    You can visualize your current configuration at edit time and at runtime with the TargetIndicatorBoundaryVisualizer component.

    The following sections describe how to use the TargetIndicatorBoundaryVisualizer to visualize the configured boundary.

    Add visualizer

    Attach the TargetIndicatorBoundaryVisualizer component by clicking the Add Boundary Visualizer button on the bottom of the target indicator manager component.

    The TargetIndicatorManager component attached to a GameObject.

    Once added, you can remove the visualizer by clicking the Remove Boundary Visualizer button.

    Note

    Only BoundaryType.Padded and BoundaryType.Absolute can be visualized with the built in visualizer.

    Edit visualizer

    Boundary line color and line width properties are available in the inspector to modify the boundary visualizer. This is useful if you have multiple target indicator managers and need to distinguish easily between them.

    The TargetIndicatorManager component attached to a GameObject.

    Create a custom visualizer

    The provided TargetIndicatorBoundaryVisualizer uses a LineRenderer and draws the boundary in front of the camera, updating it's position as the camera moves. This is to reduce the dependency on better screen space solutions such as Unity UI, UI Toolkit, or a custom shader. Because it uses a LineRenderer it can have strange side effects if your camera moves quickly or has an extreme field of view.

    If your project has a special camera configuration where the provided boundary visualizer does not adequately meet your needs, you can create your own boundary visualizer. Use the data provided by TargetIndicatorManager.Rectangle or TargetIndicatorManager.Ellipse to draw your own visualizer.

    API

    The following sections describe how to use the target indicator manager API.

    Add a target

    Add targets to track and receive updates for target indicators The max number of targets that can be tracked at once is 100. TryAddTarget can only fail if you try to add more than 100 targets or try to add a null target. To get the current number of tracked targets use TrackedTargetsCount. If you need more than 100 (god bless), you can use multiple target indicator managers.

    To add a target to track use TryAddTarget as shown in the following code example:

    public void TryAddTargetExample(TargetIndicatorManager manager, Transform target)
    {
        var wasAdded = manager.TryAddTarget(target, out var targetIndicator);
    
        if (!wasAdded)
        {
            // Failed to add target because max limit of targets is reached.
            // Handle error.
            return;
        }
    
        // Successfully added target.
    }
    

    Keep track of the returned TargetIndicator.TrackedIndicatorId in the out parameter to know which target indicator corresponds to a target.

    Remove a target

    Remove targets to have the target indicator manager stop tracking and sending updates for a target. Removing a target can only fail if you pass an invalid TargetIndicatorId.

    To remove a target from being tracked use TryRemoveTarget as shown in the following code example:

    public void TryRemoveTargetExample(TargetIndicatorManager manager, TargetIndicatorId id)
    {
        var wasRemoved = manager.TryRemoveTarget(id);
    
        if (!wasRemoved)
        {
            // Failed to remove target because the target indicator ID was invalid.
            // Handle error.
            return;
        }
    
        // Successfully removed target.
    }
    

    Remove all targets

    To remove all targets at once use RemoveAllTargets as shown in the following code example:

    public void RemoveAllTargetsExample(TargetIndicatorManager manager)
    {
        manager.RemoveAllTargets();
    
        // Successfully removed all tracked targets.
    }
    
    Tip

    If you just want to stop receiving updates you can disable the TargetIndicatorManager component. Re-enabling it will resume tracking targets.

    Get a target

    To check if a target indicator is being tracked use TryGetTargetIndicator as shown in the following code example:

    public void GetATargetIndicatorExample(
        TargetIndicatorManager manager, TargetIndicatorId id)
    {
        var didGet = manager.TryGetTargetIndicator(id, out var targetIndicator);
    
        if (!didGet)
        {
            // Failed to get target because target indicator ID was invalid.
            // Handle error.
            return;
        }
    
        // Successfully received target indicator.
    }
    

    Get screen pose

    If you want to get the screen pose of any world space position that adherers to your current boundary, use GetScreenPose. This can be useful if you want to control your own life cycle, or if you only need a one time update, or you need infrequent updates for a target's screen pose.

    The following code example shows you how to use GetScreenPose:

    public void GetScreenPoseExample(
        TargetIndicatorManager manager, Vector3 worldSpacePosition)
    {
        var screenPose = manager.GetScreenPose(worldSpacePosition, out var isOutsideBoundary);
    
        // screenPose.position.x = horizontal axis coordinate in screen space (pixels).
        // screenPose.position.y = vertical axis coordinate in screen space (pixels).
        // screenPose.position.z = depth from the camera in world space (meters).
    
        // screenPose.rotation = direction to target in screen space.
    
        // isOutsideBoundary = true if the screenPose.position is outside the configured
        // boundary. The depth from the camera is ignored in this check.
    }
    

    Is outside boundary

    Each TargetIndicator contains an IsOutsideBoundary property that you can use to know if a target indicator is outside the configured boundary. If you want to know if any world space position is outside the current boundary use TargetIndicatorManager.TargetIndicators.TargetIndicatorManager.IsOutsideBoundary as shown in the following example:

    public void IsOutsideBoundaryExample(
        TargetIndicatorManager manager, Vector3 worldSpacePosition)
    {
        var isOutsideBoundary = manager.IsOutsideBoundary(worldSpacePosition);
    
        // isOutsideBoundary = true if the screen pose coordinates are outside the
        // configured boundary. The depth from the camera is ignored in this check.
    }
    
    Important

    For BoundaryType.CompassTape and BoundaryType.Unbounded the IsOutsideBoundary property will always be false. BoundaryType.Compass requires knowledge about your specific compass tape setup to determine if it's outside the boundary.

    Refer to Compass tape boundary check to learn how you can check if the target indicator is outside of your compass tape boundary.

    Life cycle events

    While enabled, the TargetIndicatorManager component will check for changes every frame. If any anchors were added, updated, or removed, TargetIndicatorManager will invoke TargetIndicatorsAdded, TargetIndicatorsUpdated, and TargetIndicatorsRemoved events respectively.

    Each event passes a ReadOnlySpan that wraps an underlying array. This avoids heap allocations and slices the array to the size of changes that occurred since the last time the events were invoked.

    The following code example shows how you can subscribe to these events and iterate over the spans of target indicators they pass:

    public void LifeCycleExample(TargetIndicatorManager manager)
    {
        manager.TargetIndicatorsAdded += OnTargetIndicatorsAdded;
        manager.TargetIndicatorsUpdated += OnTargetIndicatorsUpdated;
        manager.TargetIndicatorsRemoved += OnTargetIndicatorsRemoved;
    }
    
    void OnTargetIndicatorsAdded(ReadOnlySpan<TargetIndicator> added)
    {
        foreach (var targetIndicator in added)
        {
            var screenPose = targetIndicator.ScreenPose;
        }
    }
    
    void OnTargetIndicatorsUpdated(ReadOnlySpan<TargetIndicator> updated)
    {
        foreach (var targetIndicator in updated)
        {
            var updatedScreenPose = targetIndicator.ScreenPose;
        }
    }
    
    void OnTargetIndicatorsRemoved(ReadOnlySpan<TargetIndicatorId> removed)
    {
        foreach (var targetIndicatorId in removed)
        {
            // TargetIndicatorId was removed.
        }
    }
    

    You can disable the TargetIndicatorManager component to stop receiving life cycle events and control your own life cycle and rely on the TargetIndicatorManager exclusively for pose and boundary checks.

    Using multiple target indicator managers

    Some applications might require multiple boundary types or multiple configurations. You can have multiple TargetIndicatorManager components in your scene to get more complex designs. For example if you use BoundaryType.CompassTape you will only get indicators to display on the compass tape but might not know what target they correspond to.

    You can have a second TargetIndicatorManager that is configured to use BoundaryType.Unbounded that marks the same target with a matching icon as used in your compass tape visualizer to indicate which target represents which marker in your compass tape indicator. The Samples provides an example of how to configre multiple target indicator managers.

    In this article
    Back to top Generated by DocFX