Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b0fd6d5
first draft of UIToolkit
Elfi0Kuhndorf May 30, 2024
4dc0326
WIP adjusting NetworkManagerUI with UI toolkit and starting to connec…
Elfi0Kuhndorf May 31, 2024
cf5cffc
moving UI Toolkit related files into UI toolkit folder, also NetworkM…
Elfi0Kuhndorf Jun 3, 2024
313f90f
deleting old UI from scenes and adjusting NetworkManagerUI.cs to make…
Elfi0Kuhndorf Jun 3, 2024
dc99897
Merge branch 'develop' into feat/multiplayer-use-cases-ui-toolkit-imp…
Elfi0Kuhndorf Jun 10, 2024
9c92673
removed some redundant assets and code in NetworkManagerUI.cs
Elfi0Kuhndorf Jun 10, 2024
307c832
revert scene and NetworkManagerUI prefab changes
Elfi0Kuhndorf Jun 10, 2024
62a9924
fix NetworkManagerUI.prefab and fix style classes
Elfi0Kuhndorf Jun 10, 2024
e03b3e1
add SetNetworkPortAndAddress back to StartClient function
Elfi0Kuhndorf Jun 11, 2024
352d5cd
removed unneeded UIElementsSchema
Elfi0Kuhndorf Jun 11, 2024
ac01dca
simplify code in NetworkManagerUI.cs and include UIElementsUtils
Elfi0Kuhndorf Jun 12, 2024
c2c1a3a
make the UI some percentage bit smaller (adjusting the uxml sheet of …
Elfi0Kuhndorf Jun 13, 2024
a6dbfc5
utilizing UIElementsUtils for simplifying code, (WIP) trying to highl…
Elfi0Kuhndorf Jun 13, 2024
e2e960a
added highlights for buttons, fixed serverOverlay (adjusting size), a…
Elfi0Kuhndorf Jun 18, 2024
9ae7d23
WIP adjusting script to use old method for highlighting and disabling
Elfi0Kuhndorf Jun 25, 2024
91c8087
WIP hovering does still not work
Elfi0Kuhndorf Jun 25, 2024
925ff8a
fix: added validation of IP and port address
Elfi0Kuhndorf Jun 27, 2024
82a8ed4
fix: color changes through button interactions work now
Elfi0Kuhndorf Jun 27, 2024
b979ee9
fix: added Styleclasses instead of color fields to implement color ch…
Elfi0Kuhndorf Jun 28, 2024
ac4bf2d
fix: scaling of UI changes depending on screen size
Elfi0Kuhndorf Jul 1, 2024
2c9ff06
Merge branch 'develop' into feat/multiplayer-use-cases-ui-toolkit-imp…
Elfi0Kuhndorf Jul 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,875 changes: 46 additions & 1,829 deletions Experimental/MultiplayerUseCases/Assets/Common/Prefabs/NetworkManagerUI.prefab

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &6992812452127128618
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 4183923682644896075}
- component: {fileID: 4429985340320126427}
m_Layer: 0
m_Name: NetworkManagerUI_Toolkit
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &4183923682644896075
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6992812452127128618}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &4429985340320126427
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 6992812452127128618}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 19102, guid: 0000000000000000e000000000000000, type: 0}
m_Name:
m_EditorClassIdentifier:
m_PanelSettings: {fileID: 11400000, guid: bb97c42b0da64184d8f6ef64e0c20845, type: 2}
m_ParentUI: {fileID: 0}
sourceAsset: {fileID: 9197481963319205126, guid: 54eac630072f2954eab32aa476089063, type: 3}
m_SortingOrder: 1

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,45 +1,57 @@
using System;
using Unity.Netcode.Transports.UTP;
using Unity.Networking.Transport;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityEngine.UIElements;
using Button = UnityEngine.UIElements.Button;

namespace Unity.Netcode.Samples.MultiplayerUseCases.Common
{

[RequireComponent(typeof(UIDocument))]
public class NetworkManagerUI : MonoBehaviour
{
[SerializeField] Button m_ServerButton;
[SerializeField] Button m_HostButton;
[SerializeField] Button m_ClientButton;
[SerializeField] Button m_DisconnectButton;
[SerializeField] Button m_QuitSceneButton;
[SerializeField] Color32 m_HighlightedButtonColor;
[SerializeField] Color32 m_DisabledButtonColor;
[SerializeField] Color32 m_ButtonOutlineColor;
[SerializeField] Color32 m_ButtonOutlineHighlightedColor;
[SerializeField] Color32 m_ButtonOutlineDisabledColor;
[SerializeField] string m_SelectionScreenSceneName;
[SerializeField] GameObject m_ServerOnlyOverlay;
const string k_DefaultIP = "127.0.0.1";
const string k_DefaultServerListenAddress = "0.0.0.0"; //note: this is not safe for real world usage and would limit you to IPv4-only addresses, but this goes out the scope of this sample.
const ushort k_DefaultPort = 7979;

VisualElement m_Root;
TextField m_AddressInputField;
TextField m_PortInputField;
Button m_ServerButton;
Button m_HostButton;
Button m_ClientButton;
Button m_DisconnectButton;
Button m_QuitSceneButton;
Button[] m_Buttons;

[SerializeField]
string m_SelectionScreenSceneName;
[SerializeField]
GameObject m_ServerOnlyOverlay;

void Awake()
{
m_ServerButton.onClick.AddListener(StartServer);
m_HostButton.onClick.AddListener(StartHost);
m_ClientButton.onClick.AddListener(StartClient);
m_DisconnectButton.onClick.AddListener(Disconnect);
m_QuitSceneButton.onClick.AddListener(QuitScene);
var uiDocument = GetComponent<UIDocument>();
m_Root = uiDocument.rootVisualElement;
m_AddressInputField = UIElementsUtils.SetupStringField("IPAddressField", string.Empty, k_DefaultIP, OnAddressChanged, m_Root);
m_PortInputField = UIElementsUtils.SetupStringField("PortField", string.Empty, k_DefaultPort.ToString(), OnPortChanged, m_Root);
m_ServerButton = UIElementsUtils.SetupButton("ServerButton", StartServer, true, m_Root, "Server", "Starts the Server");
m_HostButton = UIElementsUtils.SetupButton("HostButton", StartHost, true, m_Root, "Host", "Starts the Host");
m_ClientButton = UIElementsUtils.SetupButton("ClientButton", StartClient, true, m_Root, "Client", "Starts the Client");
m_DisconnectButton = UIElementsUtils.SetupButton("DisconnectButton", Disconnect, false, m_Root, "Disconnect", "Disconnects participant from session");
UIElementsUtils.SetupButton("QuitSceneButton", QuitScene, true, m_Root, "Quit Scene", "Quits scene and brings you back to the scene selection screen");
m_Buttons = new Button[] { m_ServerButton, m_HostButton, m_ClientButton };
}

void Start()
{
SetButtonStateAndColor(m_DisconnectButton, false, false);
m_ServerOnlyOverlay.gameObject.SetActive(false);
}

void StartServer()
{
SetNetworkPortAndAddress(ushort.Parse(m_PortInputField.value), m_AddressInputField.value, k_DefaultServerListenAddress);
NetworkManager.Singleton.StartServer();
EnableAndHighlightButtons(m_ServerButton, false);
SetButtonStateAndColor(m_DisconnectButton, false, true);
Expand All @@ -48,13 +60,15 @@ void StartServer()

void StartHost()
{
SetNetworkPortAndAddress(ushort.Parse(m_PortInputField.value), m_AddressInputField.value, k_DefaultServerListenAddress);
NetworkManager.Singleton.StartHost();
EnableAndHighlightButtons(m_HostButton, false);
SetButtonStateAndColor(m_DisconnectButton, false, true);
}

void StartClient()
{
SetNetworkPortAndAddress(ushort.Parse(m_PortInputField.value), m_AddressInputField.value, k_DefaultServerListenAddress);
NetworkManager.Singleton.StartClient();
EnableAndHighlightButtons(m_ClientButton, false);
SetButtonStateAndColor(m_DisconnectButton, false, true);
Expand Down Expand Up @@ -87,6 +101,44 @@ void OnSceneLoaded(Scene loadedScene, LoadSceneMode arg1)
}
}

void OnAddressChanged(ChangeEvent<string> evt)
{
string newAddress = evt.newValue;
ushort currentPort = ushort.Parse(m_PortInputField.value);

if (string.IsNullOrEmpty(newAddress) || !NetworkEndPoint.TryParse(newAddress, currentPort, out NetworkEndPoint networkEndPoint))
{
Debug.LogError($"IP address '{newAddress}', is not valid. Reverting IP address to {k_DefaultIP}");
m_AddressInputField.value = k_DefaultIP;
return;
}

SetNetworkPortAndAddress(currentPort, newAddress, k_DefaultServerListenAddress);
}

void OnPortChanged(ChangeEvent<string> evt)
{
if (!ushort.TryParse(evt.newValue, out ushort newPort) || !NetworkEndPoint.TryParse(m_AddressInputField.value, newPort, out NetworkEndPoint networkEndPoint))
{
Debug.LogError($"Port '{evt.newValue}' is not valid. Reverting port to {k_DefaultPort}");
m_PortInputField.value = k_DefaultPort.ToString();
return;
}

SetNetworkPortAndAddress(newPort, m_AddressInputField.value, k_DefaultServerListenAddress);
}

static void SetNetworkPortAndAddress(ushort port, string address, string serverListenAddress)
{
var transport = NetworkManager.Singleton.GetComponent<UnityTransport>();
if (transport == null) //happens during Play Mode Tests
{
return;
}

transport.SetConnectionData(address, port, serverListenAddress);
}

void EnableAndHighlightButtons(Button buttonToHighlight, bool enable)
{
foreach (var button in m_Buttons)
Expand All @@ -97,15 +149,19 @@ void EnableAndHighlightButtons(Button buttonToHighlight, bool enable)

void SetButtonStateAndColor(Button button, bool highlight, bool enable)
{
ColorBlock colors = button.colors;
colors.disabledColor = highlight ? m_HighlightedButtonColor
: m_DisabledButtonColor;
button.colors = colors;
button.interactable = enable;

var buttonOutline = button.transform.Find("Outline").GetComponent<Image>();
buttonOutline.color = enable ? m_ButtonOutlineColor : m_ButtonOutlineDisabledColor;
buttonOutline.color = highlight ? m_ButtonOutlineHighlightedColor : buttonOutline.color;
if (enable)
{
button.RemoveFromClassList("UseCaseButtonHighlight");
button.AddToClassList("UseCaseButton");
}

else
{
button.RemoveFromClassList("UseCaseButton");
button.AddToClassList(highlight ? "UseCaseButtonHighlight" : "UseCaseButton");
}

button.SetEnabled(enable);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System;
using System.IO;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine.UIElements;

namespace Unity.Netcode.Samples.MultiplayerUseCases.Common
{
/// <summary>
/// An utility class for common UIElements setup method
/// </summary>
public static class UIElementsUtils
{
#if UNITY_EDITOR
static readonly string k_UIFilesPathInTemplate = Path.Combine("Assets", Path.Combine("Editor", "UI"));

/// <summary>
/// Loads an UXML file from an editor folder
/// </summary>
/// <param name="fileName">The name fo the file to load</param>
/// <returns></returns>
public static VisualTreeAsset LoadUXML(string fileName)
{
string path = $"{Path.Combine(k_UIFilesPathInTemplate, fileName)}.uxml";
return AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(path);
}
#endif

/// <summary>
/// Initializes a Button
/// </summary>
/// <param name="buttonName">Name of the button in the document</param>
/// <param name="onClickAction">method to execute on click</param>
/// <param name="isEnabled">Is the button enabled?</param>
/// <param name="parent">Parent of the button</param>
/// <param name="text">Text to display on the button</param>
/// <param name="tooltip">Tooltip of the button</param>
/// <param name="showIfEnabled">Enables the element if it supposed to be enabled</param>
/// <returns>The initialized button</returns>
public static Button SetupButton(string buttonName, Action onClickAction, bool isEnabled, VisualElement parent, string text = "", string tooltip = "", bool showIfEnabled = true)
{
Button button = parent.Query<Button>(buttonName);
button.SetEnabled(isEnabled);
button.clickable = new Clickable(() => onClickAction?.Invoke());
button.text = text;
button.tooltip = string.IsNullOrEmpty(tooltip) ? button.text : tooltip;

if (showIfEnabled && isEnabled)
{
Show(button);
}

return button;
}

/// <summary>
/// Initializes an EnumField
/// </summary>
/// <typeparam name="T">Type of the values in the EnumField</typeparam>
/// <param name="enumName">Name of the EnumField in the document</param>
/// <param name="text">Text to display on the EnumField's label</param>
/// <param name="onValueChanged">method to execute when the vlaue of EnumField changes</param>
/// <param name="parent">Parent of the EnumField</param>
/// <param name="defaultValue">Default vlaue of the Enumfield</param>
/// <returns>The initialized EnumField</returns>
public static EnumField SetupEnumField<T>(string enumName, string text, EventCallback<ChangeEvent<Enum>> onValueChanged, VisualElement parent, T defaultValue) where T : Enum
{
EnumField uxmlField = parent.Q<EnumField>(enumName);
uxmlField.label = text;
uxmlField.Init(defaultValue);
uxmlField.value = defaultValue;
uxmlField.RegisterCallback(onValueChanged);
return uxmlField;
}

/// <summary>
/// Initializes a Toggle
/// </summary>
/// <param name="name">Name of the toggle</param>
/// <param name="label">Text of the toggle's label</param>
/// <param name="text">Text of the toggle</param>
/// <param name="defaultValue">Default value of the toggle</param>
/// <param name="onValueChanged">Method to call when the value of the toggle changes</param>
/// <param name="parent">Parent of the toggle</param>
/// <returns>The initializedToggle</returns>
public static Toggle SetupToggle(string name, string label, string text, bool defaultValue, EventCallback<ChangeEvent<bool>> onValueChanged, VisualElement parent)
{
Toggle uxmlField = parent.Q<Toggle>(name);
uxmlField.label = label;
uxmlField.text = text;
uxmlField.value = defaultValue;
uxmlField.SetEnabled(true);
uxmlField.RegisterCallback(onValueChanged);
return uxmlField;
}

/// <summary>
/// Initializes an IntegerField
/// </summary>
/// <param name="name">Name of the IntegerField</param>
/// <param name="value">Start value of the IntegerField</param>
/// <param name="onValueChanged">Method to call when the value changes</param>
/// <param name="parent">Parent of the IntegerField</param>
/// <returns>The initialized IntegerField</returns>
public static IntegerField SetupIntegerField(string name, int value, EventCallback<ChangeEvent<int>> onValueChanged, VisualElement parent)
{
IntegerField uxmlField = parent.Q<IntegerField>(name);
uxmlField.value = value;
uxmlField.SetEnabled(true);
uxmlField.RegisterCallback(onValueChanged);
return uxmlField;
}

/// <summary>
/// Initializes an StringField
/// </summary>
/// <param name="name">Name of the StringField</param>
/// <param name="label">Text of the StringField's label</param>
/// <param name="value">Start value of the StringField</param>
/// <param name="onValueChanged">Method to call when the value changes</param>
/// <param name="parent">Parent of the StringField</param>
/// <returns>The initialized StringField</returns>
public static TextField SetupStringField(string name, string label, string value, EventCallback<ChangeEvent<string>> onValueChanged, VisualElement parent)
{
TextField uxmlField = parent.Q<TextField>(name);
uxmlField.label = label;
uxmlField.value = value;
uxmlField.SetEnabled(true);
uxmlField.RegisterCallback(onValueChanged);
return uxmlField;
}

/// <summary>
/// Makes a visual element invisible
/// </summary>
/// <param name="element">The element</param>
public static void Hide(VisualElement element)
{
element.style.display = DisplayStyle.None;
}

/// <summary>
/// Makes a visual element visible
/// </summary>
/// <param name="element">The element</param>
public static void Show(VisualElement element)
{
element.style.display = DisplayStyle.Flex;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading