This section covers a bit of css (web development). If you are familiar with front-end web development then this probably isn't going to be very helpful. If you are not familiar with css, then there are far better resources online to teach good css styling approaches than this gitbook. We highly recommend that you check out Traversy Media's CSS Crash Course For Absolute Beginners if you are just getting started. We include a bit of styling in this gitbook for completeness, but there are far superior resources.
We will style the TitleScreen in this section by creating and adding selectors to our TitleScreenUI USS, and at the end of this page we will provide code for the final TitleScreenManager, TitleScreen, HostGameScreen, JoinGameScreen, ManualConnectScreen uxmls/cVEs as well as the final TitleScreenUI USS.
We won't show instructions for all the views but after seeing how styling works on 1 screen it should not be difficult to put the pieces together If you have questions on any of the other views reach out on Moetsi Discord.
Open up the UI Builder and open up the TitleScreenManager uxml
Currently we have .screen as a selector applied to most parent views
We want our backgrounds to be white, so we are going to update that selector to be white as well
Select the .screen selector from StyleSheets and set the background equal to #FFFFFF (white)
What gives, why isn't the backgrounds white?!
We initially set the .screen selector, but then the specific inline backgrounds we gave each of our views overridesthe initial selector assignment
Let's remove all the inline backgrounds for all the views
TitleScreen
HostGameScreen
JoinGameScreen
ManualConnectScreen
To do this, for each of the views select the uxml from Project in Library, then select the cVE in Hierarchy, then navigate to Background in Inspector and right-click on the "Color" property and click "unset"
Make sure to save each screen, then return to TitleScreenManager
Click "preview" and navigate the screens to make sure all the colors have been updated
Now we are ready to style TitleScreen, open up the uxml
Delete the 3 buttons
We will reimplement them later but currently they are in the way
The way we are going to separate this view is by "header" and "main content"
We want the "header" to always be at the top of the view and always be a certain height
"main content" will be right below the header and be a max width (550 px) and shrink if the resolution is more narrow
This way we can accommodate narrow views but also not make our buttons and content super wide if we are on desktop
We will be extracting most of our styles to TitleScreenUI USS. This is because a lot of our styles are shared amount screens. Take a look at the flow diagram below to better understand this.
Going through and styling our first view will feel a bit tedious, but once we have our style sheet and components set up it is much simpler to set up other screens. We literally copy and paste Hierarchy components into other views.
Let's get started
Drag two VisualElements (VEs) into "screen" in the Hierarchy
Name the top one "header" and the bottom one "main-content"
Styling the header
First we will style header
Position
Position = Absolute
Top = 0
Flex
Grow = 1
Direction = row
Align
Align Items = center
Justify Content = space between
Size
Width = 100%
Height = 108px
Now let's extract these styles and make it a new selector called ".header"
To do this type in ".header" under Style Class List under StyleSheet in Inspector and then click "Extract Inlined Styles to New Class"
You might have noticed that we did "space between" for the Justify Content
That will allows us to keep both our logo and Quit button "hugged" to the sides of the screen regardless of its size
Now within header let's add 2 standard elements
button
VisualElement
Place the VisualElement above the button in the Hierarchy
The order doesn't really matter, but this order is the format we like to use at Moetsi
Name the VisualElement "#moetsi-logo"
You could name this Visual Element whatever you want, of course. We do not refer to it anywhere by name so the name doesn't matter (we are just describing what we put there)
Now let's style the moetsi-logo in the Inspector
Flex
Grow = 1
Size
Width
Max = 200px
Height = 68px
Margin
Left = 10px
Scale Mode (in Background) = scale-to-fit
Now import an image you would like to use, in our case we imported our logo
Drag the image into the Assets/UI folder
In UI Builder go to Background and select the image to set it as the Background
We can also add an SVG
We chose not to include the SVG in the main gitbook flow because the SVG package currently causes some visual artifacts and we think that png is for now still the way to go.
vs.
But just in case you'd like to know, these the steps to use a vector file as a Background image:
Import your SVG into Assets/UI
Select the SVG and checkout the top of the Inspector
In the bottom dropdown "Generated Asset Type" select "UI Toolkit Vector Image"
Click "apply"
Select your SVG and you're all set!
Great, now let's extract the selector (by typing .logo into the "Style Class List" field under StyleSheet in the Inspector)
Now let's move onto our quit button
Select the Button under #header within TitleScreen.uxml in Hierarchy
Name the button "#quit-button"
In the Inspector update the styling
Button
Text = Quit
Size
Width = 120px
Height = 68px
Styling continued
Margin
All = 0px (to set to 0)
Right = 10px
Padding
All = 0px
Text
Size = 24
Color = #00000
Background
Color = Alpha = 0 (make it see through)
Border
Color = #000000
Width = 3px
Radius = 10px
Not necessary but we will be creating a folder UI/Fonts for Moetsi typography and assigning our button the font
Now let's select quit-button and extract our inline selector
and name it ".quit-button"
To do this: select quit-button in Hierarchy, then type ".quit-button" into the Style Class List field under StyleSheet then click "Extract Inlined Styles to New Class"
Next, in StyleSheets, click into the input field that reads "Add new selector..."
Type in ".quit-button:hover" and press enter
Next, type in ".quit-button:active" and press enter
These selectors will change the styling of our button when the user "hovers" or clicks ("active") on the button
Select .quit-button:hover and configure in the Inspector
Border
Color = #000000
Width = 5px
Radius = 9px
Select .quit-button:active and configure in the Inspector
Text
Color = #FFFFFF
Background
Color = #000000 (put the alpha to 100%)
Save and go to the editor, hit play, and interact with the button
Great we have our header fully styled
Styling main content
Open UI Builder back up
Let's first start by adding the items we will be styling in main-content
Go to Library > Standard tab and drag these into main-content as children:
Label (name = "title")
VisualElement (name = "join-local-title")
VisualElement (name = "joining-local-game-view")
Button (name = "manual-connect")
Label (name = "or")
Button (name = "host-local-game")
Currently main-content is overlapping the header because the header is positioned absolute, so let's style main-content:
Position
Position = Absolute
Top = 108
Align
Align Items = center
Size
Width = 100% (find the % by clicking the button to the right of the text field)
Width Max = 550px
Margin & Padding
Padding
Left = 20
Right = 20
Extract the inline styles to make a new selector .main-content
Next, let's style #title (within #main-content)
Label
Text = 3D XR Asteroids
Margin & Padding
Top = 100px
Font
Font = (change if you like)
Size = 64
Extract the inline styles and make selector ".title"
Next, let's style #join-local-title
Size
Width = 100% (find the % by clicking the button to the right of the text field)
Margin & Padding
Top = 100px
Extract the inline styles to make a new selector .section-title-container
Drag a label into join-local-title and name it "#join-a-local-game"
Label
Text = Join A Local Game
Margin & Padding
Padding = 0px (all)
Font
Size = 36
Extract the inline styles to make a new selector .section-title
If you run into issues with the canvas (like in the image above) expand the size of the canvas in the TitleScreen uxml
Next, let's style the joining-local-game-view
Change the name to "local-games-list-container" in the Name field in Inspector
Align
Align Items = center
Justify Content = center
Size
Width = 100%
Height = 200px
Border
Color = #000000
Width = 5px
Radius = 10px
Drag a standard ListView element into local-games-list-container (we will make this functional in the next section "Creating a List")
ListView
Name = local-games-list
Item Height = 100
Size
Width = 100%
Height = 100%
Now let's style manual-connect
First, add a Button
Text = Connect to a Local Game
Now back to manual-connect:
Flex
Wrap = wrap
Size
Width = 100%
Height = 68px
Margin & Padding
Margin
All = 0px (to clear out)
Top = 21px
Padding = 0px (all)
Font
Size =24
Color = #96BFD0
Wrap = normal
Background
Color = 0 Alpha (see through)
Border
Color = #96BFD0
Width = 5px
Radius = 10px
Extract the inline styling and make a new selector named ".blue-button"
Now go back to StyleSheets and add 2 new selectors by typing in the below into the "Add new selector..." field (press enter between each selector):
.blue-button:hover
.blue-button:active
Click on .blue-button:hover to style it:
Border
Width = 7px (all)
Radius = 9px (all)
Now style .blue-button:active like so:
Text
Color = #FFFFFF
Background
Color = #96BFD0
Next, let's style "or" (within #manual-connect in Hierarchy)
Label
Text = Or
Margin & Padding
Margin
Top = 20px
Padding = 0px (all)
Text
Size = 36
Next, let's style #host-local-game:
Button
Text = Host A Local Game
Flex
Wrap = wrap
Size
Width = 100%
Height = 120px
Margin & Padding
Margin
All = 0 px (to reset)
Top = 36px
Padding = 0px (all)
Text
Size = 36
Color = #A0C272
Background
Color = 0 alpha (see through)
Border
Color = #A0C272
Width = 5px
Radius = 10px
Extract the inline styles into a new selector .green-button
Go to StyleSheets and add 2 new selectors (by typing in the name below into "Add new selector..." and hitting enter)
.green-button:hover
.green-button:active
.green-button:hover
Border
Width = 7px
Radius = 9px
.green-button:active
Font
Color = #FFFFFF
Background
Color = #A0C272
Now update the #screen Visual Element (not the selector)
Align
Align Items = center
No pic here, we believe in you 💪
Play around with the size of the canvas to see how the different elements resize for different screen sizes
Navigate to the editor, hit play, and hover and click on the buttons
We now have our styled TitleScreen uxml
We added Visual Elements
We styled the Visual Elements and made selectors from the inline styles
For the buttons we made additional selectors for hover and active states
All the code you need for the uxml and USS in this project
In terms of learning new things, there isn't much to gain by styling even more screens together here in this section. Instead, we provide you with the code snippets to paste into your uxml and cVE files and the TitleScreenUI USS below.
You will notice that the first child in the updated uxmls for TitleScreen, HostGameScreen, JoinGameScreen, and ManualConnectScreen is a "ScrollView" Visual Element. This Visual Element exists because Unity does not currently support "overflow" (where the view automatically becomes scrollable if the contents of the UI are too big for a display). The implication of not supporting overflow is that small screen sizes "cut off" UI. So to account for all screen sizes and avoid this, we wrap all of our views in a ScrollView so that if your resolution is too small to fit all UI content, we will be able to scroll to navigate it all.
Why add ScrollView now, so late in the section? That's because having ScrollView in the Hierarchy makes it a little harder to navigate the Hierarchy in UI Builder, so that's why we do the ScrollView wrap step last.
Paste the code snippet below into your TitleScreenUI.uss file:
Paste the code snippet below into your HostGameScreen.cs file (cVE):
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;
public class HostGameScreen : VisualElement
{
TextField m_GameName;
TextField m_GameIp;
TextField m_PlayerName;
String m_HostName = "";
IPAddress m_MyIp;
public new class UxmlFactory : UxmlFactory<HostGameScreen, UxmlTraits> { }
public HostGameScreen()
{
this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
}
void OnGeometryChange(GeometryChangedEvent evt)
{
//
// PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
//
m_GameName = this.Q<TextField>("game-name");
m_GameIp = this.Q<TextField>("game-ip");
m_PlayerName = this.Q<TextField>("player-name");
// CLICKING CALLBACKS
this.Q("launch-host-game")?.RegisterCallback<ClickEvent>(ev => ClickedHostGame());
this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
}
void ClickedHostGame()
{
Debug.Log("clicked host game");
}
}
Paste the code snippet below into your JoinGameScreen.uxml file:
Paste the code snippet below into your JoinGameScreen.cs file (cVE):
using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Net.NetworkInformation;
using System.Collections;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using Unity.Entities;
using Unity.NetCode;
using UnityEngine.SceneManagement;
public class JoinGameScreen : VisualElement
{
Label m_GameName;
Label m_GameIp;
TextField m_PlayerName;
String m_HostName = "";
IPAddress m_MyIp;
public new class UxmlFactory : UxmlFactory<JoinGameScreen, UxmlTraits> { }
public JoinGameScreen()
{
this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
}
void OnGeometryChange(GeometryChangedEvent evt)
{
//
// PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
//
m_GameName = this.Q<Label>("game-name");
m_GameIp = this.Q<Label>("game-ip");
m_PlayerName = this.Q<TextField>("player-name");
// CLICKING CALLBACKS
this.Q("launch-host-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());
this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
}
void ClickedJoinGame()
{
Debug.Log("clicked client game");
}
}
Paste the code snippet below into your ManualConnectScreen.uxml file:
Paste the code snippet below into your ManualConnectScreen.cs file (cVE):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
public class ManualConnectScreen : VisualElement
{
TextField m_GameIp;
TextField m_PlayerName;
string m_HostName = "";
IPAddress m_MyIp;
public new class UxmlFactory : UxmlFactory<ManualConnectScreen, UxmlTraits> { }
public ManualConnectScreen()
{
this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
}
void OnGeometryChange(GeometryChangedEvent evt)
{
//
// PROVIDE ACCESS TO THE FORM ELEMENTS THROUGH VARIABLES
//
m_GameIp = this.Q<TextField>("game-ip");
m_PlayerName = this.Q<TextField>("player-name");
// CLICKING CALLBACKS
this.Q("launch-connect-game")?.RegisterCallback<ClickEvent>(ev => ClickedJoinGame());
this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
}
void ClickedJoinGame()
{
Debug.Log("clicked manual connect");
}
}
Make sure to save all of those files you just updated
If you chose not to use the provided Moetsi font and Moetsi logo from earlier in this section, you will need to go back and update those sections with your different fonts and images, or else you will get errors
In our testing nearly every time after this update you must quit Unity and reopen the project to have it update.
Hit play and check out navigation between the screens
We now have our styled TitleScreenManager
Github branch link:
git clone https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/
cd Unity-DOTS-Multiplayer-XR-Sample
git checkout 'Styling-a-View'