Style a View

Code and workflows to style the Project title screen with UI Builder

What you'll develop on this page

How to style the title screen from this Project using UI Builder and have it be mobile-friendly.

Github branch link: https://github.com/moetsi/Unity-DOTS-Multiplayer-XR-Sample/tree/Styling-a-View

Styling Title Screen

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 overrides the 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:

.screen {
    flex-grow: 1;
    font-size: 20px;
    align-items: stretch;
    background-color: rgb(255, 255, 255);
}

.quit-button {
    margin-right: 10px;
    margin-left: 0;
    width: 120px;
    height: 68px;
    font-size: 24px;
    color: rgb(0, 0, 0);
    background-color: rgba(0, 0, 0, 0);
    border-left-color: rgb(0, 0, 0);
    border-right-color: rgb(0, 0, 0);
    border-top-color: rgb(0, 0, 0);
    border-bottom-color: rgb(0, 0, 0);
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
}

.quit-button:hover {
    background-color: rgba(0, 0, 0, 0);
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}

.quit-button:active {
    background-color: rgb(0, 0, 0);
    color: rgb(255, 255, 255);
}

.main-menu-button {
    width: 219px;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
}

.header {
    height: 108px;
    position: absolute;
    flex-direction: row;
    justify-content: space-between;
    flex-grow: 1;
    top: 0;
    width: 100%;
    align-items: center;
}

.main-content {
    position: absolute;
    top: 108px;
    left: auto;
    right: auto;
    bottom: auto;
    max-width: 550px;
    width: 100%;
    height: auto;
    align-items: center;
    padding-left: 20px;
    padding-right: 20px;
    -unity-font: url('/Assets/UI/Fonts/HV.ttf');
    color: rgb(0, 0, 0);
}

.title {
    font-size: 64px;
    margin-top: 100px;
    -unity-font: url('/Assets/UI/Fonts/Cervo.otf');
    color: rgb(0, 0, 0);
}

.section-title-container {
    width: 100%;
    margin-top: 100px;
}

.section-title {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 36px;
    color: rgb(0, 0, 0);
}

.blue-button {
    width: 100%;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 0;
    margin-right: 0;
    margin-top: 21px;
    margin-bottom: 0;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    height: 68px;
    border-left-color: rgb(150, 191, 208);
    border-right-color: rgb(150, 191, 208);
    border-top-color: rgb(150, 191, 208);
    border-bottom-color: rgb(150, 191, 208);
    background-color: rgba(0, 0, 0, 0);
    font-size: 24px;
    color: rgb(150, 191, 208);
    flex-wrap: wrap;
    white-space: normal;
}

.blue-button:hover {
    border-left-width: 7px;
    border-right-width: 7px;
    border-top-width: 7px;
    border-bottom-width: 7px;
    background-color: rgba(0, 0, 0, 0);
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
}

.blue-button:active {
    background-color: rgb(150, 191, 208);
    color: rgb(255, 255, 255);
}

.green-button {
    left: auto;
    right: auto;
    margin-left: 0;
    margin-right: 0;
    margin-top: 36px;
    margin-bottom: 0;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    width: 100%;
    height: 120px;
    color: rgb(160, 194, 114);
    font-size: 36px;
    background-color: rgba(0, 0, 0, 0);
    border-left-color: rgb(160, 194, 114);
    border-right-color: rgb(160, 194, 114);
    border-top-color: rgb(160, 194, 114);
    border-bottom-color: rgb(160, 194, 114);
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    flex-wrap: wrap;
    white-space: normal;
}

.green-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 7px;
    border-right-width: 7px;
    border-top-width: 7px;
    border-bottom-width: 7px;
    background-color: rgba(0, 0, 0, 0);
}

.green-button:active {
    background-color: rgb(160, 194, 114);
    color: rgb(255, 255, 255);
}

.data-section {
    width: 100%;
    background-color: rgb(255, 255, 255);
}

.data-section-input {
    flex-direction: column-reverse;
    height: 49px;
    margin-left: 0;
    margin-right: 0;
    margin-top: 36px;
    margin-bottom: 0;
    font-size: 36px;
    color: rgb(160, 194, 114);
    border-bottom-width: 4px;
    border-bottom-color: rgb(160, 194, 114);
    border-top-color: rgb(160, 194, 114);
    border-left-color: rgb(160, 194, 114);
    border-right-color: rgb(160, 194, 114);
    width: auto;
    background-color: rgba(0, 0, 0, 0);
}

.data-section-label {
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    color: rgb(160, 194, 114);
}

.unity-base-field {
}

.screen-scroll-container {
    flex-grow: 1;
    background-color: rgb(255, 255, 255);
}

.quit-game-button {
    flex-direction: column-reverse;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    margin-left: 10px;
    margin-right: 0;
    margin-top: 0;
    margin-bottom: 0;
    background-color: rgba(0, 0, 0, 0);
    border-left-width: 3px;
    border-right-width: 3px;
    border-top-width: 3px;
    border-bottom-width: 3px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    width: 219px;
    height: 68px;
    border-left-color: rgb(255, 255, 255);
    border-right-color: rgb(255, 255, 255);
    border-top-color: rgb(255, 255, 255);
    border-bottom-color: rgb(255, 255, 255);
    font-size: 24px;
    color: rgb(255, 255, 255);
    white-space: normal;
}

.quit-game-button:hover {
    border-top-left-radius: 9px;
    border-bottom-left-radius: 9px;
    border-top-right-radius: 9px;
    border-bottom-right-radius: 9px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
}

.quit-game-button:active {
    background-color: rgb(255, 255, 255);
    color: rgb(0, 0, 0);
}

.logo {
    flex-grow: 1;
    max-width: 200px;
    height: 68px;
    margin-left: 10px;
    background-image: url('/Assets/UI/Moetsi-logo-hq.png');
    -unity-background-scale-mode: scale-to-fit;
}

.local-games-list-container {
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 200px;
    border-left-width: 5px;
    border-right-width: 5px;
    border-top-width: 5px;
    border-bottom-width: 5px;
    border-top-left-radius: 10px;
    border-bottom-left-radius: 10px;
    border-top-right-radius: 10px;
    border-bottom-right-radius: 10px;
    border-left-color: rgb(0, 0, 0);
    border-right-color: rgb(0, 0, 0);
    border-top-color: rgb(0, 0, 0);
    border-bottom-color: rgb(0, 0, 0);
}

.local-games-list {
    width: 100%;
    height: 100%;
}

.or {
    color: rgb(0, 0, 0);
    margin-top: 20px;
    padding-left: 0;
    padding-right: 0;
    padding-top: 0;
    padding-bottom: 0;
    font-size: 36px;
}

.HostGameScreen {
    align-items: center;
}

.JoinGameScreen {
    align-items: center;
}

.ManualConnectScreen {
    align-items: center;
}
  • Paste the code snippet below into your TitleScreenManager.uxml file:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <ui:Template name="TitleScreen" src="TitleScreen.uxml" />
    <ui:Template name="HostGameScreen" src="HostGameScreen.uxml" />
    <ui:Template name="JoinGameScreen" src="JoinGameScreen.uxml" />
    <ui:Template name="ManualConnectScreen" src="ManualConnectScreen.uxml" />
    <Style src="TitleScreenUI.uss" />
    <TitleScreenManager name="TitleScreenManager" class="screen" style="left: auto; top: auto; position: relative; right: auto; bottom: auto;">
        <ui:Instance template="TitleScreen" name="TitleScreen" class="screen" />
        <ui:Instance template="HostGameScreen" name="HostGameScreen" class="screen" style="display: none;" />
        <ui:Instance template="JoinGameScreen" name="JoinGameScreen" class="screen" style="display: none;" />
        <ui:Instance template="ManualConnectScreen" name="ManualConnectScreen" class="screen" style="display: none;" />
    </TitleScreenManager>
</ui:UXML>
  • Paste the code snippet below into your TitleScreenManager.cs (cVE) file:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

public class TitleScreenManager : VisualElement
{
    VisualElement m_TitleScreen;
    VisualElement m_HostScreen;
    VisualElement m_JoinScreen;
    VisualElement m_ManualConnectScreen;
    
    public new class UxmlFactory : UxmlFactory<TitleScreenManager, UxmlTraits> { }

    public TitleScreenManager()
    {
        this.RegisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    void OnGeometryChange(GeometryChangedEvent evt)
    {
        m_TitleScreen = this.Q("TitleScreen");
        m_HostScreen = this.Q("HostGameScreen");
        m_JoinScreen = this.Q("JoinGameScreen");
        m_ManualConnectScreen = this.Q("ManualConnectScreen");

        m_TitleScreen?.Q("host-local-game")?.RegisterCallback<ClickEvent>(ev => EnableHostScreen());
        m_TitleScreen?.Q("join-local-game")?.RegisterCallback<ClickEvent>(ev => EnableJoinScreen());
        m_TitleScreen?.Q("manual-connect")?.RegisterCallback<ClickEvent>(ev => EnableManualScreen());

        m_HostScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());
        m_JoinScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());
        m_ManualConnectScreen?.Q("back-button")?.RegisterCallback<ClickEvent>(ev => EnableTitleScreen());

        this.UnregisterCallback<GeometryChangedEvent>(OnGeometryChange);
    }

    public void EnableHostScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.Flex;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.None;

    }

    public void EnableJoinScreen()
    {
        Debug.Log("Enable join screen trigger");
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.Flex;
        m_ManualConnectScreen.style.display = DisplayStyle.None;
    }

    public void EnableManualScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.None;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.Flex;
    }

    public void EnableTitleScreen()
    {
        m_TitleScreen.style.display = DisplayStyle.Flex;
        m_HostScreen.style.display = DisplayStyle.None;
        m_JoinScreen.style.display = DisplayStyle.None;
        m_ManualConnectScreen.style.display = DisplayStyle.None;
    }

}
  • Paste the code snippet below into your TitleScreen.uxml file:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <ui:VisualElement name="screen" class="screen HostGameScreen">
            <ui:VisualElement name="header" class="header">
                <ui:IMGUIContainer name="moetsi-logo" class="logo" />
                <ui:Button text="Quit" name="quit-button" class="quit-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" name="title" class="title" />
                <ui:VisualElement name="join-local-title" class="section-title-container">
                    <ui:Label text="Join A Local Game" name="join-a-local-game" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="local-games-list-container" class="local-games-list-container">
                    <ui:ListView name="local-games-list" item-height="100" class="local-games-list" />
                </ui:VisualElement>
                <ui:Button text="Connect to a Local Game" name="manual-connect" class="button blue-button" />
                <ui:Label text="Or" name="or" class="or" />
                <ui:Button text="Host A Local Game" name="host-local-game" class="button green-button" />
            </ui:VisualElement>
        </ui:VisualElement>
    </ui:ScrollView>
</ui:UXML>
  • Paste the code snippet below into your HostGameScreen.uxml file:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <HostGameScreen name="HostGameScreen" class="screen HostGameScreen">
            <ui:VisualElement name="header" class="header">
                <ui:Button text="Main Menu" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content" style="top: 108px; left: auto; position: absolute;">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Host a Local Game" display-tooltip-when-elided="True" name="section-title" class="section-title" style="color: rgb(160, 194, 114);" />
                </ui:VisualElement>
                <ui:VisualElement name="game-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostNameValue" text="GameName" name="game-name" class="data-section-input" />
                    <ui:Label text="Your Game Name" display-tooltip-when-elided="True" name="game-name-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostIPValue" text="127.0.0.1" name="game-ip" readonly="false" class="data-section-input" />
                    <ui:Label text="Your Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="PlayerName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Host Game" display-tooltip-when-elided="True" name="launch-host-game" class="green-button" />
            </ui:VisualElement>
        </HostGameScreen>
    </ui:ScrollView>
</ui:UXML>
  • 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:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen">
        <JoinGameScreen name="JoinGameScreen" class="screen JoinGameScreen">
            <ui:VisualElement name="header" class="header" style="height: 108px; top: 0;">
                <ui:Button text="Back Button" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Join A Local Game" display-tooltip-when-elided="True" name="section-title" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="game-name-container" class="data-section">
                    <ui:Label text="ThereHostName" display-tooltip-when-elided="True" name="game-name" class="data-section-input" style="border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0;" />
                    <ui:Label text="Game Name" display-tooltip-when-elided="True" name="game-name-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:Label text="192.168.156" display-tooltip-when-elided="True" name="game-ip" class="data-section-input" style="border-left-width: 0; border-right-width: 0; border-top-width: 0; border-bottom-width: 0;" />
                    <ui:Label text="Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="YourHostName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Join Game" display-tooltip-when-elided="True" name="launch-join-game" class="blue-button" style="height: 120px;" />
            </ui:VisualElement>
        </JoinGameScreen>
    </ui:ScrollView>
</ui:UXML>
  • 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:

<ui:UXML xmlns:ui="UnityEngine.UIElements" xmlns:uie="UnityEditor.UIElements" editor-extension-mode="False">
    <Style src="TitleScreenUI.uss" />
    <ui:ScrollView class="screen-scroll-container">
        <ManualConnectScreen name="ManualConnectScreen" class="screen ManualConnectScreen">
            <ui:VisualElement name="header" class="header">
                <ui:Button text="Back Button" display-tooltip-when-elided="True" name="back-button" class="quit-button main-menu-button" />
            </ui:VisualElement>
            <ui:VisualElement name="main-content" class="main-content">
                <ui:Label text="3D XR Asteroids" display-tooltip-when-elided="True" name="title" class="title" />
                <ui:VisualElement name="section-title-container" class="section-title-container">
                    <ui:Label text="Manually Connect" display-tooltip-when-elided="True" name="section-title" class="section-title" />
                </ui:VisualElement>
                <ui:VisualElement name="game-ip-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="HostIPValue" text="127.0.0.1" name="game-ip" readonly="false" class="data-section-input" style="background-color: rgb(255, 255, 255);" />
                    <ui:Label text="Game&apos;s IP Address" display-tooltip-when-elided="True" name="game-ip-label" class="data-section-label" />
                </ui:VisualElement>
                <ui:VisualElement name="player-name-container" class="data-section">
                    <ui:TextField picking-mode="Ignore" value="PlayerNameValue" text="PlayerName" name="player-name" readonly="false" class="data-section-input" style="border-left-color: rgb(150, 191, 208); border-right-color: rgb(150, 191, 208); border-top-color: rgb(150, 191, 208); border-bottom-color: rgb(150, 191, 208);" />
                    <ui:Label text="Your Player Name" display-tooltip-when-elided="True" name="player-name-label" class="data-section-label" style="color: rgb(150, 191, 208);" />
                </ui:VisualElement>
                <ui:Button text="Join Game" display-tooltip-when-elided="True" name="launch-connect-game" class="blue-button" style="height: 120px;" />
            </ui:VisualElement>
        </ManualConnectScreen>
    </ui:ScrollView>
</ui:UXML>
  • 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'

Last updated