Streaming is available in most browsers,
and in the Developer app.
-
Add accessibility to your Unity games
Learn how you can make your Unity games accessible on Apple platforms using our open source Accessibility plug-in. Follow along as we add support for assistive technologies like VoiceOver and Switch Control to a sample Unity game project. We'll show you how you can automatically scale text with Dynamic Type, support interface accommodations like reduced transparency or increased contrast, and more.
Resources
Related Videos
WWDC22
-
Download
♪ ♪ Hello everyone, my name is Eric, and I'm excited to tell you how to add accessibility to your Unity games.
Accessibility is about allowing everyone to use our products. And today marks a huge leap forward for accessible gaming with the Apple Accessibility plug-in for Unity developers. We will focus on three Apple technologies for making your games accessible.
VoiceOver is a screen reader which helps users who are blind or low vision. It reads items on the screen and provides custom gestures for users to interact with controls.
Switch control, which allows someone with low motor control to use an external switch for device interaction. And dynamic type, which allows people to set the text size according to their reading ability.
To get started, clone the repository and build all Apple's plug-ins using the build script in the root of the repository. This will produce a Build folder that is ready for integration into your Unity projects. Finally, add the Accessibility plug-in to your own project using the Unity Package Manager. For more details, look at the documentation in the repository and watch the video about all of Apple's Unity plug-ins, called "Plug-in and play: Add Apple frameworks to your Unity game projects" Now that you have the plug-in, I will guide you through three areas. First is accessibility elements. This allows you to add support to assistive technologies like VoiceOver or Switch Control for your games. Next is Dynamic Type. We create this easy-to-use utility that helps you scale text according to user preferences. And UI Accommodations. These are utilities that helps you read other user preferences. Let's start with accessibility elements.
I built a simple card game to help illustrate this concept. You might see that you should tap the "flip" button to draw two random cards. However, VoiceOver would not read the text on screen and an external switch would not tap the button since these are just pixels on a screen right now. We need to help the system understand what can be interacted with. Accessibility elements define the things that assistive technologies can interact with.
The text, cards, and the button on screen should be accessibility elements. And we can describe each element with a label. VoiceOver will read each label so the user can understand what's on the screen.
And if the game supports multiple languages, we should localize these labels as well.
Now VoiceOver can describe what is on the screen, but it doesn't recognize that there's a button that can be tapped. We can use another property called "traits" to inform the system of the element's type. We should add the "Button" trait here. Now, VoiceOver will read "Flip button," and an external switch can control this button.
We can also add "Static Text" trait on our text elements. The "Static Text" trait is usually given to labels and text areas so that VoiceOver can provide a better text interaction experience.
And there are many more traits beyond "Button" and "Static Text" that you can explore.
So what trait should we use for the cards? Well, we don't need to use traits on every accessibility element. Our cards don't need any traits. However, there's still a part of each card that VoiceOver is not aware of: the face value. There's another property that we can use for this called "Value." Once we add a "Value" for each card, VoiceOver will now read "Card 1 with value 3 of clubs, card 2 with value Ace of clubs." And that's it. Now that you understand the basics, let's open Unity to see how to add them to our project.
Here I am in a Unity Editor for this game. I have already added the Apple Accessibility plugin to this project.
First we have the usual scene objects like camera, direct light, and a UI Canvas. Under the canvas, we have two text elements and a button. After that, we have two game objects for cards.
Each is composed of two mesh components, each rendering the front and back texture for the card on each side. Let's start by defining our accessibility elements. To do this, we need the Accessibility Node component from this plugin.
Select all the objects in the hierarchy that should be accessible.
And add the Accessibility Node component to make them accessibility elements.
Next, we add labels. Select the card object, go to "Accessibility Node" component on the right, and find the "Label" field.
And make sure the label field checkbox is checked to provide a custom label.
Then type "Card 1." And the same for Card 2.
Text and buttons need labels too, but we don't have to provide an explicit accessibility labels for them if we are using the standard controls from Unity UI. The plug-in already has default implementations for those.
Next, we need to add a trait to our button.
Select the flip button and change the "Traits" from "None" to "Button." Select the two text elements...
And change "Traits" to "static text." Great. Lastly, we need to set an accessibility Value for the card faces. Since the cards are randomly drawn, I need to add a script to set the Value dynamically.
Select the two cards and add a new script called AccessibleCard.
First, in one of my other C Sharp files, I already have an enum for all the card faces called Playing Card. In my new AccessibleCard mono behavior script, we have a variable for the card type and a boolean for whether the card is facing up or down.
So now let's add accessibilityValue to these cards. First, we get the accessibilityNode component attached to this gameObject.
Next, set the accessibilityValue delegate to a function that returns the card face value dynamically.
Inside this function, if the card is covered, we return the "covered" for the accessibilityValue. Or if not covered, we will enumerate all card faces and return a description for each, like "Ace of Spades." And that's it. Now let's build our project and see it in action. Here's our game. Let's turn on VoiceOver.
Automated voice: VoiceOver on. Eric's Game. Card 2, covered. Eric: I could swipe right to move to the next element. Automated voice: Card 1, covered.
Eric's card game.
Flip the cards.
Flip. Button. Eric: You see that all five objects are now accessible through VoiceOver, which is awesome. To tap the button when VoiceOver is on, do a double-tap. Automated voice: Flip. Eric: Let's check the cards again.
Automated voice: Card 1, Two of Clubs.
Card 2, Ace of Clubs. Eric: VoiceOver now reads the updated card faces correctly. Cool. So we just made our game accessible to millions of VoiceOver users who can now fall in love with it. And people who use external switch control can also play our game. So that was accessibility elements. Next, let's talk about Dynamic Type.
Games can be difficult to play for many people because text is too small to read. On iOS and tvOS, everyone can choose the right text size for their reading ability in Settings.
With the Accessibility Plugin, you can read this setting to make sure the text in your game is displayed at the expected size.
Let's take a look at our game example to see how we can use Dynamic Type.
Create a mono behavior script called DynamicTextSize.cs. In the start function, first store the default text size into a variable.
Then inside the OnEnable function, subscribe to setting changed event using AccessibilitySettings. onPreferredTextSizesChanged. This allows us to update the UI as soon as the user changes the text setting.
Next, inside the settingsChanged function, first read the PreferredContentSizeMultiplier. Then multiply by your original text size and assign it back to the text element.
Inside Unity Editor, select all game objects that have a Text element. And add the DynamicTextSize component that we just created.
Now our game is all set for Dynamic type support. Before we see the result in action, first I am going to show you a trick to quickly test Dynamic Type in your games. Open Settings, and find Control Center.
Scroll down until you see Text Size, and add it to Control Center.
Now we can adjust text sizes quickly by opening Control Center and change the text size options.
Great–as I change the text size, our game adjusts font sizes in real time.
The text percentage value shown in Control Center is exactly what we are reading from that multiplier. You can also adopt this setting on non-text objects. For example, I can swap the card face assets to Large Print when the size is increased.
First I create a script called DynamicCardFaces. Then same thing as before, subscribe to the TextSizeChanged event.
Instead of reading a multiplier, I read an enum of text size category that is mapped to the ticks on the Control Center slider. I could swap the asset whenever someone selects a larger text size.
And I simply choose between a regular material and a large print material. Now if we select a really large size...
Users would see a large print version of the cards, which are great card faces that are much easier to read for people with low vision.
Lastly, I want to talk to you about UI accommodation settings that you can access with this plug-in.
The first setting is Reduce Transparency. When this setting is on, the background is turned opaque, instead of a blur or transparent effect. It can help improve legibility when those effects makes text hard to read. To check this preference, call AccessibilitySettings. IsReduceTransparencyEnabled.
Next, the Increase Contrast setting. Notice how the switches have a darker grey that helps them stand out, making controls easier to recognize across the entire device.
You can increase contrast for your own UI when this is enabled by checking this setting using AccessibilitySettings. IsIncreaseContrastEnabled.
Next, the Reduce Motion setting. Some people are sensitive to motion like we have in this card flip animation.
We should remove that animation when Reduce Motion is enabled.
Let's look at the code to do this.
In our CardController script, we have this Flip function. First we check if user's reduce motion setting is on. If not on, we should flip the card by invoking an animation through Coroutine. Otherwise we just set the rotation, and no animations. And that's it. Now people who are sensitive to motion would enjoy our game. To recap, get started with the Apple Accessibility plugin by cloning the GitHub repository linked in this session's resources.
Add accessibility elements so that people can use VoiceOver and Switch Control with your games.
Adapt your text size with Dynamic Type.
And check for UI accommodations so everyone can have a great experience with your game.
Thank you so much for joining me. We look forward to seeing how you make games available for everyone with a great accessibility experience.
-
-
7:43 - PlayingCard enum
public enum PlayingCard { AceOfSpade, AceOfClubs, AceOfDiamonds }
-
7:53 - AccessibleCard class
using Apple.Accessibility; public class AccessibleCard : MonoBehaviour { public PlayingCard cardType; public bool isCovered; void Start() { var accessibilityNode = GetComponent<AccessibilityNode>(); accessibilityNode.accessibilityValueDelegate = () => { if (isCovered) { return "covered"; } if (cardType == PlayingCard.AceOfSpades) { return "Ace of Spades"; } } } }
-
10:35 - DynamicCardFaces
public class DynamicCardFaces : MonoBehaviour { public Material RegularMaterial; public Material LargeMaterial; void OnEnable() { AccessibilitySettings.onPreferredTextSizesChanged = _settingsChanged; } void _settingsChanged() { var shouldUseLarge = AccessibilitySettings.PreferredContentSizeCategory >= ContentSizeCategory.AccessibilityMedium; GetComponent<Renderer>().material = shouldUseLarge ? RegularMaterial : LargeMaterial; } }
-
10:36 - Dynamic Type
using UnityEngine.UI; public class DynamicTextSize : MonoBehaviour { int originalSize; void Start() { originalSize = GetComponent<Text>().textSize; } void OnEnable() { AccessibilitySettings.onPreferredTextSizesChanged = _settingsChanged; } void _settingsChanged() { GetComponent<Text>().textSize = (int)(originalSize * AccessibilitySettings.PreferredContentSizeMultiplier); } }
-
14:54 - Reduce motion
using Apple.Accessibility; public class CardController : MonoBehaviour { public void Flip() { var reduceMotionOn = !AccessibilitySettings.IsReduceMotionEnabled; if (!reduceMotionOn) { StartCoroutine(Animate()); } else { transform.rotation = Quaternion.identify; } } IEnumerator Animate() { } }
-
-
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.