TransWikia.com

Unity: How to connect to server with status in dialog

Game Development Asked by Zechy on November 2, 2021

I am trying to build in unity a basic logon screen. Now I’m building on something if the connection will have delay or so… There will be a dialog, where the progress of connection to server will be written… Like Attempt 1/3, Attempt 2/3…

I have Dialog class, which is building a Dialog window from prefab:

    public class Dialog
    {
        private const string TitleComponent = "Title";
        private const string ContentComponent = "Content";

        private readonly IPrefabFactory _dialogFactory = new DialogFactory(false);
        private readonly GameObject _dialog;

        public event ClickHandler Click;

        public string Title
        {
            set => GetTextComponent(TitleComponent).text = value;
        }

        public string Content
        {
            set => GetTextComponent(ContentComponent).text = value;
        }

        public Vector3 Position
        {
            set
            {
                _dialog.transform.position = value;
                _dialog.transform.localPosition = value;
            }
            get => _dialog.transform.position;
        }

        public bool Active
        {
            set => _dialog.SetActive(value);
            get => _dialog.activeSelf;
        }

        public Dialog(string name)
        {
            _dialog = _dialogFactory.Create();
            _dialog.name = name;
            _dialog.GetComponent<DialogBehaviour>().Dialog = this;
            SetToCanvas();
        }

        public void OnClick()
        {
            Click?.Invoke();
        }
        
        private void SetToCanvas()
        {
            var uiCanvas = Object.FindObjectOfType<Canvas>();
            _dialog.transform.SetParent(uiCanvas.transform, false);
            Position = new Vector3(0, 0, 0);
        }

        private TextMeshProUGUI GetTextComponent(string name)
        {
            foreach (Transform child in _dialog.transform)
            {
                if (child.name == name)
                {
                    return child.gameObject.GetComponent<TextMeshProUGUI>();
                }
            }
            
            throw new Exception($"Component with name `{name}` was not found.");
        }
    }

And I have some server "Manager", where the connection process is running.

        private void Connect()
        {
            Dialog.Active = true;
            var attempts = 1;
            do
            {
                Dialog.Content = $"Connecting to server... ({attempts++}/{MaxAttempts})";
                try
                {
                    if (Client.CurrentState == ComponentState.Ready)
                    {
                        Client.Connect();
                    }
                    else
                    {
                        Client.Reconnect();
                    }
                }
                catch (ConnectionException)
                {
                    attempts++;
                }
            } while (!Client.IsConnected && attempts <= MaxAttempts);

            if (!Client.IsConnected)
            {
                Dialog.Content = "Connection with server could not be established.";
                throw new ConnectionException("");
            }

            Dialog.Active = false;
        }

        private void CheckConnection()
        {
            if (Client.CurrentState == ComponentState.Ready)
            {
                Connect();
            }
            else
            {
                if (Client.CheckConnection()) return;
                Connect();
            }
        }

        protected Message SendAndReceive(Message message)
        {
            CheckConnection();
            Message response = null;
            
            try
            {
                response = Client.Send(message);
            }
            catch (MessageSendException)
            {
                Dialog.Active = true;
                var attempts = 2;
                do
                {
                    Dialog.Content = $"Sending message... ({attempts}/{MaxAttempts})";
                    try
                    {
                        response = Client.TryReceive();
                    }
                    catch (MessageSendException)
                    {
                        attempts++;
                    }
                } while (response == null && attempts <= MaxAttempts);
            }

            if (response == null)
            {
                Dialog.Content = "Server did not send any response. Probably connection is lost.";
                throw new MessageSendException("");
            }

            Dialog.Active = false;

            return response;
        }

I want to provide some output to screen about whats hapening in that dialog class. But, where is the problem. Everything is so much fast, that the dialog can’t even render on the start and during all attempts to connect to server, the main thread is just stucked to the time, when the connection ends.

I was finding something about how to make this happen like in another thread or so… Or find coroutines, but I have no idea how to make this work…

It should work like this:

  • Render dialog.
  • Write that the connection attempt is running.
  • During another attempts update the status.
  • If successful dismiss dialog.

After all this happen, something similar should happen with the sending. How to make this working without stucking the main thread and make all steps run smoothly one after another?

One Answer

You didn't post any of the code which actually sends the login attempt to the server, as all of that is abstracted away in an object "Client". And there are quite a lot of ways to do networking in Unity. But most network APIs don't block the current thread until there is an answer. They usually send a message and then return immediately. The actual response is then processed by a handler function when it finally arrives.

You are already using that pattern for the click events for your button. Your networking API likely uses a similar pattern for asynchronous operations.

So the usual pattern you would use is:

  1. Send a request using an asynchronous networking API which returns immediately. Such APIs will usually require a callback function which is to be called when there is a response, and often a second one to be called in case of an error.
  2. Keep running updates and keep rendering the dialog (which Unity does automatically for you). Perhaps showing some animated indicator that there is a pending connection attempt.
  3. When a response from the server arrives, or the network API reports an error, it executes a callback function.
  4. That callback function notifies the Dialog of the result of the network operation
  5. During the next Update() of the Dialog, it notices that there is a notification from the network API and changes the UI accordingly

Answered by Philipp on November 2, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP