Update UI using AR Foundation
Code and workflows to optimize and update the UI in our AR enabled project

What you'll develop on this page

Shooting down AR player
Getting shot down by desktop player
We will update the UI to both fit better and to dynamically update the instructions shown to AR players.

Updating UI

Updating the instructions

We are going to update the instructions on the bottom-left of the screen in ARPlatformInitializer.
We are going to grab each of the instructions and update it with new text once we know that the project is running on an AR platform.
    Paste the code snippet below into ARPlatformInitializer.cs:
1
using System.Collections;
2
using System.Collections.Generic;
3
using UnityEngine;
4
using UnityEngine.XR.ARFoundation;
5
using Unity.Entities;
6
using Unity.NetCode;
7
using UnityEngine.UIElements;
8
9
public class ARPlatformInitializer : MonoBehaviour
10
{
11
[SerializeField] GameObject m_Session;
12
[SerializeField] GameObject m_SessionOrigin;
13
14
//This is how we will grab access to the UI elements we need to update
15
public UIDocument m_GameUIDocument;
16
private VisualElement m_GameManagerUIVE;
17
private VisualElement m_BottomLeft;
18
private Label m_1stInstruction;
19
private Label m_2ndInstruction;
20
private Label m_3rdInstruction;
21
private Label m_4thInstruction;
22
23
void OnEnable()
24
{
25
//We set the labels that we will need to update
26
m_GameManagerUIVE = m_GameUIDocument.rootVisualElement;
27
m_BottomLeft = m_GameManagerUIVE.Q<VisualElement>("bottom-left");
28
m_4thInstruction = m_GameManagerUIVE.Q<Label>("instructions-4");
29
m_3rdInstruction = m_GameManagerUIVE.Q<Label>("instructions-3");
30
m_2ndInstruction = m_GameManagerUIVE.Q<Label>("instructions-2");
31
m_1stInstruction = m_GameManagerUIVE.Q<Label>("instructions-1");
32
}
33
34
IEnumerator Start() {
35
if ((ARSession.state == ARSessionState.None) ||
36
(ARSession.state == ARSessionState.CheckingAvailability))
37
{
38
yield return ARSession.CheckAvailability();
39
}
40
41
if (ARSession.state == ARSessionState.Unsupported)
42
{
43
//If we AR is unsupported we disable both GameObjects
44
m_SessionOrigin.SetActive(false);
45
m_Session.SetActive(false);
46
}
47
else
48
{
49
//If AR is supported we create our IsARPlayerComponent singleton in ClientWorld
50
foreach (var world in World.All)
51
{
52
if (world.GetExistingSystem<ClientSimulationSystemGroup>() != null)
53
{
54
world.EntityManager.CreateEntity(typeof(IsARPlayerComponent));
55
}
56
}
57
58
//Due to a UI Toolkit bug we cannot currently update existing labels without getting wacky behavior
59
// https://forum.unity.com/threads/updating-labels-causes-a-gap-on-first-ui-view.1049081/
60
//So we will remove all labels and attach new ones to our bottom left container
61
m_BottomLeft.Remove(m_4thInstruction);
62
m_BottomLeft.Remove(m_3rdInstruction);
63
m_BottomLeft.Remove(m_2ndInstruction);
64
m_BottomLeft.Remove(m_1stInstruction);
65
66
//Now that our container is empty we make 3 new labels for our 3 new instructions
67
Label instruction1 = new Label();
68
Label instruction2 = new Label();
69
Label instruction3 = new Label();
70
71
//Now we add our instruction-text class to our labels so they have the same styling as before
72
instruction1.AddToClassList("instruction-text");
73
instruction2.AddToClassList("instruction-text");
74
instruction3.AddToClassList("instruction-text");
75
76
//Now we update the instruction text in the labels
77
instruction3.text = "Tap with 1 finger to spawn and shoot";
78
instruction2.text = "Move device to move player";
79
instruction1.text = "Tap with 3 fingers to self-destruct";
80
81
//Because we flex grow upwards we start with the bottom instruction (instruction 1) and then add the rest
82
m_BottomLeft.Add(instruction1);
83
m_BottomLeft.Add(instruction2);
84
m_BottomLeft.Add(instruction3);
85
}
86
}
87
}
Copied!
Updating ARPlatformInitializer
    In MainScene drag the GameUI GameObject from Hierarchy into the Game UI Document field within the AR Platform Initializer component in Inspector when AR Session is selected in Hierarchy
Moving GameUI into ARPlatformInitializer compoinent
    Go to the BuildSettings folder, select iOS-Build, then click "Build and Run" in Inspector
    Then launch the app
Hitting "Build and run" for the iOS-Build
Updating instructions dynamically for AR players
We now have dynamic Game UI based on platform type
    We updated ARPlatformInitializer

Updating the styling

We are going to update the styling to update the padding in the footer and header. Currently, the curve and notch in newer iPhones cover up quite a bit at the top and bottom of the screen. Although adjusting the styling to account for newer iPhones is not required (well, none of this is), we are explaining this just to make your project a little easier to navigate. We will also update our logo to use an SVG.
Moetsi Logo SVG.svg
6KB
Image
Moetsi Logo SVG
    Add the SVG file to the UI folder
      With your SVG selected go to the Inspector and select
        "UI Toolkit Vector Image" in the drop-down list for Generated Asset Type
        Target Resolution 2160p
        Click "Apply"
Adding Moetsi Logo SVG to UI/ as a UI Toolkit Vector Image
    Paste the code snippet below into TitleScreenUI USS:
1
.screen {
2
flex-grow: 1;
3
font-size: 20px;
4
align-items: stretch;
5
background-color: rgb(255, 255, 255);
6
}
7
.quit-button {
8
margin-right: 10px;
9
margin-left: 0;
10
width: 120px;
11
height: 68px;
12
font-size: 24px;
13
color: rgb(0, 0, 0);
14
background-color: rgba(0, 0, 0, 0);
15
border-left-color: rgb(0, 0, 0);
16
border-right-color: rgb(0, 0, 0);
17
border-top-color: rgb(0, 0, 0);
18
border-bottom-color: rgb(0, 0, 0);
19
border-top-left-radius: 10px;
20
border-bottom-left-radius: 10px;
21
border-top-right-radius: 10px;
22
border-bottom-right-radius: 10px;
23
border-left-width: 3px;
24
border-right-width: 3px;
25
border-top-width: 3px;
26
border-bottom-width: 3px;
27
}
28
.quit-button:hover {
29
background-color: rgba(0, 0, 0, 0);
30
border-top-left-radius: 9px;
31
border-bottom-left-radius: 9px;
32
border-top-right-radius: 9px;
33
border-bottom-right-radius: 9px;
34
border-left-width: 5px;
35
border-right-width: 5px;
36
border-top-width: 5px;
37
border-bottom-width: 5px;
38
}
39
.quit-button:active {
40
background-color: rgb(0, 0, 0);
41
color: rgb(255, 255, 255);
42
}
43
.main-menu-button {
44
width: 219px;
45
margin-left: 10px;
46
margin-right: 0;
47
margin-top: 0;
48
margin-bottom: 0;
49
}
50
.header {
51
position: absolute;
52
flex-direction: row;
53
justify-content: space-between;
54
flex-grow: 1;
55
top: 0;
56
width: 100%;
57
align-items: flex-start;
58
padding-top: 85px;
59
}
60
.main-content {
61
position: absolute;
62
top: 108px;
63
left: auto;
64
right: auto;
65
bottom: auto;
66
max-width: 550px;
67
width: 100%;
68
height: auto;
69
align-items: center;
70
padding-left: 20px;
71
padding-right: 20px;
72
-unity-font: url('/Assets/UI/Fonts/HV.ttf');
73
color: rgb(0, 0, 0);
74
}
75
.title {
76
font-size: 64px;
77
margin-top: 100px;
78
-unity-font: url('/Assets/UI/Fonts/Cervo.otf');
79
color: rgb(0, 0, 0);
80
}
81
.section-title-container {
82
width: 100%;
83
margin-top: 100px;
84
}
85
.section-title {
86
padding-left: 0;
87
padding-right: 0;
88
padding-top: 0;
89
padding-bottom: 0;
90
font-size: 36px;
91
color: rgb(0, 0, 0);
92
}
93
.blue-button {
94
width: 100%;
95
padding-left: 0;
96
padding-right: 0;
97
padding-top: 0;
98
padding-bottom: 0;
99
margin-left: 0;
100
margin-right: 0;
101
margin-top: 21px;
102
margin-bottom: 0;
103
border-left-width: 5px;
104
border-right-width: 5px;
105
border-top-width: 5px;
106
border-bottom-width: 5px;
107
border-top-left-radius: 10px;
108
border-bottom-left-radius: 10px;
109
border-top-right-radius: 10px;
110
border-bottom-right-radius: 10px;
111
height: 68px;
112
border-left-color: rgb(150, 191, 208);
113
border-right-color: rgb(150, 191, 208);
114
border-top-color: rgb(150, 191, 208);
115
border-bottom-color: rgb(150, 191, 208);
116
background-color: rgba(0, 0, 0, 0);
117
font-size: 24px;
118
color: rgb(150, 191, 208);
119
flex-wrap: wrap;
120
white-space: normal;
121
}
122
.blue-button:hover {
123
border-left-width: 7px;
124
border-right-width: 7px;
125
border-top-width: 7px;
126
border-bottom-width: 7px;
127
background-color: rgba(0, 0, 0, 0);
128
border-top-left-radius: 9px;
129
border-bottom-left-radius: 9px;
130
border-top-right-radius: 9px;
131
border-bottom-right-radius: 9px;
132
}
133
.blue-button:active {
134
background-color: rgb(150, 191, 208);
135
color: rgb(255, 255, 255);
136
}
137
.green-button {
138
left: auto;
139
right: auto;
140
margin-left: 0;
141
margin-right: 0;
142
margin-top: 36px;
143
margin-bottom: 0;
144
padding-left: 0;
145
padding-right: 0;
146
padding-top: 0;
147
padding-bottom: 0;
148
width: 100%;
149
height: 120px;
150
color: rgb(160, 194, 114);
151
font-size: 36px;
152
background-color: rgba(0, 0, 0, 0);
153
border-left-color: rgb(160, 194, 114);
154
border-right-color: rgb(160, 194, 114);
155
border-top-color: rgb(160, 194, 114);
156
border-bottom-color: rgb(160, 194, 114);
157
border-left-width: 5px;
158
border-right-width: 5px;
159
border-top-width: 5px;
160
border-bottom-width: 5px;
161
border-top-left-radius: 10px;
162
border-bottom-left-radius: 10px;
163
border-top-right-radius: 10px;
164
border-bottom-right-radius: 10px;
165
flex-wrap: wrap;
166
white-space: normal;
167
}
168
.green-button:hover {
169
border-top-left-radius: 9px;
170
border-bottom-left-radius: 9px;
171
border-top-right-radius: 9px;
172
border-bottom-right-radius: 9px;
173
border-left-width: 7px;
174
border-right-width: 7px;
175
border-top-width: 7px;
176
border-bottom-width: 7px;
177
background-color: rgba(0, 0, 0, 0);
178
}
179
.green-button:active {
180
background-color: rgb(160, 194, 114);
181
color: rgb(255, 255, 255);
182
}
183
.data-section {
184
width: 100%;
185
background-color: rgb(255, 255, 255);
186
}
187
.data-section-input {
188
flex-direction: column-reverse;
189
height: 49px;
190
margin-left: 0;
191
margin-right: 0;
192
margin-top: 36px;
193
margin-bottom: 0;
194
font-size: 36px;
195
color: rgb(160, 194, 114);
196
border-bottom-width: 4px;
197
border-bottom-color: rgb(160, 194, 114);
198
border-top-color: rgb(160, 194, 114);
199
border-left-color: rgb(160, 194, 114);
200
border-right-color: rgb(160, 194, 114);
201
width: auto;
202
background-color: rgba(0, 0, 0, 0);
203
}
204
.data-section-label {
205
padding-left: 0;
206
padding-right: 0;
207
padding-top: 0;
208
padding-bottom: 0;
209
color: rgb(160, 194, 114);
210
}
211
.unity-base-field {
212
}
213
.screen-scroll-container {
214
flex-grow: 1;
215
background-color: rgb(255, 255, 255);
216
}
217
.quit-game-button {
218
flex-direction: column-reverse;
219
padding-left: 0;
220
padding-right: 0;
221
padding-top: 0;
222
padding-bottom: 0;
223
margin-left: 10px;
224
margin-right: 0;
225
margin-top: 0;
226
margin-bottom: 0;
227
background-color: rgba(0, 0, 0, 0);
228
border-left-width: 3px;
229
border-right-width: 3px;
230
border-top-width: 3px;
231
border-bottom-width: 3px;
232
border-top-left-radius: 10px;
233
border-bottom-left-radius: 10px;
234
border-top-right-radius: 10px;
235
border-bottom-right-radius: 10px;
236
width: 219px;
237
height: 68px;
238
border-left-color: rgb(255, 255, 255);
239
border-right-color: rgb(255, 255, 255);
240
border-top-color: rgb(255, 255, 255);
241
border-bottom-color: rgb(255, 255, 255);
242
font-size: 24px;
243
color: rgb(255, 255, 255);
244
white-space: normal;
245
}
246
.quit-game-button:hover {
247
border-top-left-radius: 9px;
248
border-bottom-left-radius: 9px;
249
border-top-right-radius: 9px;
250
border-bottom-right-radius: 9px;
251
border-left-width: 5px;
252
border-right-width: 5px;
253
border-top-width: 5px;
254
border-bottom-width: 5px;
255
}
256
.quit-game-button:active {
257
background-color: rgb(255, 255, 255);
258
color: rgb(0, 0, 0);
259
}
260
.logo {
261
flex-grow: 1;
262
max-width: 300px;
263
height: 68px;
264
margin-left: 10px;
265
background-image: url('/Assets/UI/Moetsi Logo SVG.svg');
266
-unity-background-scale-mode: scale-to-fit;
267
padding-top: 10px;
268
}
269
.local-games-list-container {
270
align-items: center;
271
justify-content: center;
272
width: 100%;
273
height: 200px;
274
border-left-width: 5px;
275
border-right-width: 5px;
276
border-top-width: 5px;
277
border-bottom-width: 5px;
278
border-top-left-radius: 10px;
279
border-bottom-left-radius: 10px;
280
border-top-right-radius: 10px;
281
border-bottom-right-radius: 10px;
282
border-left-color: rgb(0, 0, 0);
283
border-right-color: rgb(0, 0, 0);
284
border-top-color: rgb(0, 0, 0);
285
border-bottom-color: rgb(0, 0, 0);
286
}
287
.local-games-list {
288
width: 100%;
289
height: 100%;
290
}
291
.or {
292
color: rgb(0, 0, 0);
293
margin-top: 20px;
294
padding-left: 0;
295
padding-right: 0;
296
padding-top: 0;
297
padding-bottom: 0;
298
font-size: 36px;
299
}
300
.HostGameScreen {
301
align-items: center;
302
}
303
.JoinGameScreen {
304
align-items: center;
305
}
306
.ManualConnectScreen {
307
align-items: center;
308
}
309
.row {
310
flex-direction: row;
311
justify-content: space-between;
312
align-items: center;
313
height: 74px;
314
}
315
.game-name-data {
316
margin-left: 17px;
317
}
318
.list-item-game-name {
319
margin-left: 0;
320
font-size: 24px;
321
margin-right: 0;
322
margin-top: 0;
323
margin-bottom: 0;
324
color: rgb(150, 191, 208);
325
padding-left: 0;
326
padding-right: 0;
327
padding-top: 0;
328
padding-bottom: 0;
329
}
330
.list-item-game-name-label {
331
padding-left: 0;
332
padding-right: 0;
333
padding-top: 0;
334
padding-bottom: 0;
335
font-size: 10px;
336
}
337
.list-item-button {
338
width: 141px;
339
height: 46px;
340
margin-left: 0;
341
margin-right: 12px;
342
margin-top: 0;
343
margin-bottom: 0;
344
border-left-color: rgba(0, 0, 0, 0);
345
border-right-color: rgba(0, 0, 0, 0);
346
border-top-color: rgba(0, 0, 0, 0);
347
border-bottom-color: rgba(0, 0, 0, 0);
348
}
349
Copied!
Updating TitleScreenUI USS
If you want to use a different SVG (not the Moetsi logo SVG), you will need to update the name of the SVG in the .logo class.
    Paste the code snippet below into GameUI USS:
1
.quit-game-button:hover {
2
border-top-left-radius: 9px;
3
border-bottom-left-radius: 9px;
4
border-top-right-radius: 9px;
5
border-bottom-right-radius: 9px;
6
border-left-width: 5px;
7
border-right-width: 5px;
8
border-top-width: 5px;
9
border-bottom-width: 5px;
10
}
11
.quit-game-button:active {
12
background-color: rgb(255, 255, 255);
13
color: rgb(0, 0, 0);
14
}
15
.quit-game-button {
16
flex-direction: column-reverse;
17
padding-left: 0;
18
padding-right: 0;
19
padding-top: 0;
20
padding-bottom: 0;
21
margin-left: 10px;
22
margin-right: 0;
23
margin-top: 0;
24
margin-bottom: 0;
25
background-color: rgba(0, 0, 0, 0);
26
border-left-width: 3px;
27
border-right-width: 3px;
28
border-top-width: 3px;
29
border-bottom-width: 3px;
30
border-top-left-radius: 10px;
31
border-bottom-left-radius: 10px;
32
border-top-right-radius: 10px;
33
border-bottom-right-radius: 10px;
34
height: 68px;
35
border-left-color: rgb(255, 255, 255);
36
border-right-color: rgb(255, 255, 255);
37
border-top-color: rgb(255, 255, 255);
38
border-bottom-color: rgb(255, 255, 255);
39
font-size: 24px;
40
color: rgb(255, 255, 255);
41
white-space: normal;
42
max-width: 140px;
43
width: 100%;
44
}
45
.game-ui-screen {
46
background-color: rgba(0, 0, 0, 0);
47
justify-content: space-between;
48
color: rgb(255, 255, 255);
49
}
50
.game-ui-header {
51
height: auto;
52
flex-direction: row;
53
align-items: flex-start;
54
}
55
.top-right-container {
56
margin-right: 10px;
57
}
58
.top-right-values {
59
color: rgb(255, 255, 255);
60
-unity-text-align: upper-right;
61
font-size: 18px;
62
}
63
.top-right-labels {
64
-unity-text-align: upper-right;
65
color: rgb(255, 255, 255);
66
font-size: 10px;
67
}
68
.spacers {
69
height: 3px;
70
}
71
.footer {
72
bottom: 0;
73
flex-direction: row;
74
justify-content: space-between;
75
position: absolute;
76
flex-grow: 1;
77
width: 100%;
78
height: auto;
79
padding-bottom: 45px;
80
}
81
.bottom-left {
82
flex-direction: column-reverse;
83
margin-left: 10px;
84
}
85
.instruction-text {
86
color: rgb(255, 255, 255);
87
white-space: normal;
88
font-size: 18px;
89
}
Copied!
Updating GameUI USS
Let's also update our PanelSettings (in the UI folder) to better handle mobile and desktop.
    In Inspector, make the following updates to PanelSettings:
      Scale Mode = Scale With Screen Size
      Screen Match Mode = Match Width Or Height
      Reference Resolution
        X = 1000
        Y = 1200
      Screen Match Mode Parameters
        Height = 1
Our UI dynamically updates when we change our Scale Mode to "scale with screen size." We choose "height" as the main driver because we have a more vertical UI in our app. If it is wider, it should not affect the scale of our UI, but if it is taller or shorter we would want our UI to scale.
Updating PanelSettings
    Now build for iOS (clicking "Build and Run" in Inspector when iOS-Build is selected from the BuildSettings folder) and take a look at our new UI updates
Shooting down AR player
Getting shot down by the desktop player
We now have a more desktop/mobile-friendly UI
    We imported an SVG
    We updated TitleScreen USS
      And updated the logo class to use the imported SVG name if necessary
    We updated GameUI USS
    We updated PanelSettings
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/ git checkout 'UI-Updates'

That's all folks!

Hopefully this gitbook was helpful to other XR developers. We plan on keeping this gitbook updates as packages and Editors are updated.
Please reach out on Discord if you have any questions or if you would like some additional sections to cover other topics.
Last modified 7mo ago