Version: Unity 6.6 Alpha (6000.6)
Language : English
Introduction to the Accessibility module
The architecture and performance of the Accessibility module

Get started with screen reader support

The screen reader support APIs are agnostic of the UI system, so they work with UI Toolkit, uGUI, custom UI frameworks, and non-UI content such as 2D or 3D objectsA 3D GameObject such as a cube, terrain or ragdoll. More info
See in Glossary
in the game world. For simplicity, this guide uses UI Toolkit, but you can adapt the code to your UI framework of choice.

Example overview

This example illustrates how to create an accessibility node, connect it to a UI Toolkit button, and test it with platform screen readers. By the end, you’ll have a button that native screen readers can read and activate.

Prerequisites

This guide is for developers familiar with the Unity Editor, UI Toolkit, and C# scripting. Before you start, get familiar with the following:

Enable the Accessibility module

The Accessibility module is enabled by default. If for some reason it’s not enabled in your project, do the following to enable it:

  1. Select Window > Package Management > Package Manager to open the Package Manager.
  2. Select the Built-in section.
  3. Select the Accessibility module.
  4. Select Enable.

Create the button

Use UI Toolkit to create a Start Game button in your sceneA Scene contains the environments and menus of your game. Think of each unique Scene file as a unique level. In each Scene, you place your environments, obstacles, and decorations, essentially designing and building your game in pieces. More info
See in Glossary
.

  1. Create a project with any template.

  2. Create a UXML file named AccessibleStartMenu.uxml with the following content:

    <ui:UXML xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" noNamespaceSchemaLocation="../../../UIElementsSchema/UIElements.xsd" editor-extension-mode="False">
        <ui:Button text="Start Game" name="startButton"/>
    </ui:UXML>
    
  3. Create a C# script named AccessibleStartMenu.cs with the following content:

using UnityEngine;
using UnityEngine.UIElements;

public class AccessibleStartMenu : MonoBehaviour
{
    Button m_Button;

    void OnEnable()
    {
        VisualElement root = GetComponent<UIDocument>().rootVisualElement;
        m_Button = root.Q<Button>("startButton");

        m_Button.clicked += OnButtonClicked;
    }

    void OnDisable()
    {
        m_Button.clicked -= OnButtonClicked;
    }

    void OnButtonClicked()
    {
        Debug.Log("Start Game button clicked");
    }
}

Create the accessibility hierarchy

The accessibility hierarchy is a semantic representation of your UI that screen readers use to discover and interact with your content. Screen readers cannot detect GameObject components or UI elements directly. They rely on this hierarchy to navigate your application. You create an AccessibilityHierarchy, then add an AccessibilityNode that represents the Start Game button.

To create the accessibility hierarchy:

  1. Add the UnityEngine.Accessibility namespace.
  2. Create an AccessibilityHierarchy instance.
  3. Create and add an AccessibilityNode to the accessibility hierarchy.
  4. Set the label, role, and state properties of the node according to the button’s text and interactable state.
// ...
using UnityEngine.Accessibility;

public class AccessibleStartMenu : MonoBehaviour
{
    // ...

    AccessibilityHierarchy m_AccessibilityHierarchy;
    AccessibilityNode m_AccessibilityNode;

    void OnEnable()
    {
        // ...

        CreateAccessibilityHierarchy();
    }

    // ...

    void CreateAccessibilityHierarchy()
    {
        // Create a new accessibility hierarchy.
        m_AccessibilityHierarchy = new AccessibilityHierarchy();

        // Create a new accessibility node with the button's text as the label
        // (what the screen readers announces).
        m_AccessibilityNode = m_AccessibilityHierarchy.AddNode(m_Button.text);

        // Set a semantic role (tells the screen reader this is a button).
        m_AccessibilityNode.role = AccessibilityRole.Button;

        // Set the state (is it currently interactable?).
        m_AccessibilityNode.state = m_Button.enabledSelf ?
            AccessibilityState.None : AccessibilityState.Disabled;
    }
}

Set the node’s screen coordinates according to the button’s size and position

To set the node’s screen coordinates:

  1. Track the button’s changes in size and position.
  2. Calculate its screen coordinates from its world coordinates and the UI scale factor.
  3. Set the node’s frame property to the calculated screen rectangle.

Update the AccessibleStartMenu.cs script as below:

public class AccessibleStartMenu : MonoBehaviour
{
    // ...

    void OnEnable()
    {
        // ...

        m_Button.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);
    }

    void OnDisable()
    {
        // ...

        m_Button.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);
    }

    // ...

    void OnGeometryChanged(GeometryChangedEvent evt)
    {
        Rect worldRect = m_Button.worldBound;
        float scale = m_Button.panel.scaledPixelsPerPoint;

        // Update the screen coordinates of the node.
        m_AccessibilityNode.frame =
            new Rect(worldRect.position * scale, worldRect.size * scale);
    }
}

Connect the node’s activation event to the button

Subscribe to the node’s invoked event, which is triggered when the user activates the node via the screen reader, then invoke the button’s NavigationSubmitEvent in the event handler.

Update the CreateAccessibilityHierarchy method in the AccessibleStartMenu.cs script as below:

public class AccessibleStartMenu : MonoBehaviour
{
    // ...

    void CreateAccessibilityHierarchy()
    {
        // ...

        // Handle when the user activates this node (e.g., double-tap).
        // Called `selected` in versions before Unity 6.3.
        m_AccessibilityNode.invoked += () =>
        {
            using var evt = NavigationSubmitEvent.GetPooled();
            evt.target = m_Button;
            m_Button.SendEvent(evt);

            return true;
        };
    }
}

Activate the accessibility hierarchy when a screen reader is enabled

  1. When the menu appears, activate the accessibility hierarchy by assigning it to AssistiveSupport.activeHierarchy.
    • When the user turns the screen reader off, AssistiveSupport.activeHierarchy is automatically set to null to free resources.
  2. Re-assign the hierarchy every time the user turns the screen reader on.
  3. When the menu disappears, remove the hierarchy by setting AssistiveSupport.activeHierarchy to null.
public class AccessibleStartMenu : MonoBehaviour
{
    // ...

    void OnEnable()
    {
        // ...

        AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy;
        AssistiveSupport.screenReaderStatusChanged += OnScreenReaderStatusChanged;
    }

    void OnDisable()
    {
        // ...

        AssistiveSupport.activeHierarchy = null;
        AssistiveSupport.screenReaderStatusChanged -= OnScreenReaderStatusChanged;
    }

    // ...

    void OnScreenReaderStatusChanged(bool enabled)
    {
        if (enabled)
        {
            AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy;
        }
        // else
        // {
        //     // This is automatically done when the user turns the screen
        //     // reader off.
        //     AssistiveSupport.activeHierarchy = null;
        // }
    }
}

You created a semantic representation (AccessibilityNode) of the visual button that screen readers can discover and interact with.

The complete AccessibleStartMenu.cs script is as follows:

using UnityEngine;
using UnityEngine.Accessibility;
using UnityEngine.UIElements;

public class AccessibleStartMenu : MonoBehaviour
{
    Button m_Button;

    AccessibilityHierarchy m_AccessibilityHierarchy;
    AccessibilityNode m_AccessibilityNode;

    void OnEnable()
    {
        VisualElement root = GetComponent<UIDocument>().rootVisualElement;
        m_Button = root.Q<Button>("startButton");

        m_Button.clicked += OnButtonClicked;
        m_Button.RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);

        CreateAccessibilityHierarchy();

        AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy;
        AssistiveSupport.screenReaderStatusChanged += OnScreenReaderStatusChanged;
    }

    void OnDisable()
    {
        m_Button.clicked -= OnButtonClicked;
        m_Button.UnregisterCallback<GeometryChangedEvent>(OnGeometryChanged);

        AssistiveSupport.activeHierarchy = null;
        AssistiveSupport.screenReaderStatusChanged -= OnScreenReaderStatusChanged;
    }

    void CreateAccessibilityHierarchy()
    {
        // Create a new accessibility hierarchy.
        m_AccessibilityHierarchy = new AccessibilityHierarchy();

        // Create a new accessibility node with the button's text as the label
        // (what the screen readers announces).
        m_AccessibilityNode = m_AccessibilityHierarchy.AddNode(m_Button.text);

        // Set a semantic role (tells the screen reader this is a button).
        m_AccessibilityNode.role = AccessibilityRole.Button;

        // Set the state (is it currently interactable?).
        m_AccessibilityNode.state = m_Button.enabledSelf ?
            AccessibilityState.None : AccessibilityState.Disabled;

        // Handle when the user activates this node (e.g., double-tap).
        // Called `selected` in versions before Unity 6.3.
        m_AccessibilityNode.invoked += () =>
        {
            using var evt = NavigationSubmitEvent.GetPooled();
            evt.target = m_Button;
            m_Button.SendEvent(evt);

            return true;
        };
    }

    void OnGeometryChanged(GeometryChangedEvent evt)
    {
        Rect worldRect = m_Button.worldBound;
        float scale = m_Button.panel.scaledPixelsPerPoint;

        // Update the screen coordinates of the node.
        m_AccessibilityNode.frame =
            new Rect(worldRect.position * scale, worldRect.size * scale);
    }

    void OnButtonClicked()
    {
        Debug.Log("Start Game button clicked");
    }

    void OnScreenReaderStatusChanged(bool isEnabled)
    {
        if (isEnabled)
        {
            AssistiveSupport.activeHierarchy = m_AccessibilityHierarchy;
        }
        // else
        // {
        //     // This is automatically done when the user turns the screen
        //     // reader off.
        //     AssistiveSupport.activeHierarchy = null;
        // }
    }
}

Attach the script

To attach the script to your scene:

  1. Create an empty GameObject in your scene and name it AccessibleStartMenu.
  2. Add a UI Document component to the GameObject.
  3. Create a Panel Settings Asset and assign it to the Panel Settings field in the InspectorA Unity window that displays information about the currently selected GameObject, asset or project settings, allowing you to inspect and edit the values. More info
    See in Glossary
    window of the UI Document component.
  4. Assign the AccessibleStartMenu.uxml file to the Source Asset field.
  5. Add the AccessibleStartMenu.cs script to the GameObject.

Test the hierarchy and node properties in Play mode

To test the hierarchy and node properties in the Unity Editor:

  1. Enter Play mode.
  2. Select Window > Accessibility > Hierarchy Viewer.
  3. Verify that the accessibility hierarchy shows the accessibility node with the correct properties.
The Accessibility Hierarchy Viewer displaying the properties of the accessibility node representing the Start Game button
The Accessibility Hierarchy Viewer displaying the properties of the accessibility node representing the “Start Game” button

Test the screen reader interaction on your target platform

To test screen reader interaction on your target platform:

  1. Build and run the application on your target platform (Android, iOS, Windows, or macOS).
  2. Get familiar with the gestures or commands of your platform’s built-in screen reader:
  3. Enable the screen reader.
  4. Navigate to the button using screen reader gestures or commands. The screen reader should be able to focus on the button and announce “Start Game, button”.
  5. Activate the button using the screen reader’s activation gesture or command. The button should respond, and the text “Start Game button clicked” should appear in the player logThe .log file created by a Standalone Player that contains a record of events, such as script execution times, the compiler version, and AssetImport time. Log files can help diagnose problems. More info
    See in Glossary
    .

Additional resources

Introduction to the Accessibility module
The architecture and performance of the Accessibility module