Search Unity

Getting Text from Field, Best Practice

Discussion in 'Scripting' started by Acoustic125, Mar 7, 2022.

  1. Acoustic125

    Acoustic125

    Joined:
    Jan 20, 2022
    Posts:
    15
    Hey, so I'm working on a standard mobile utility app, and new to Unity and C# (pretty obvious.) I'm trying to get a number from an input field to then use in calculations (there will be multiple.) After searching for a good while and experimenting with different approaches, I was finally able to get a number and print a calculation to a log. So the code below at least works. But, this definitely seems like a convoluted way to do it. I feel like I'm piecing together a bunch metric and standard sockets just to drive in a simple bolt.

    I want to learn the syntax and make sure to understand what's going on so that I don't have to ask about a bunch of small things that I should be able to figure out. I have the field set to 'decimal number' in the editor, but it looks like that only affects things the user end, and is still considered as 'text' when used the script. Also, any variation of trying to convert the string and print it outside of a separate method gives a blank or unusual result. So, what would be the simpler/better/more economical way of achieving this? Appreciate it!



    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using TMPro;
    5.  
    6. public class EmpNumbButt : MonoBehaviour
    7. {
    8.     TMP_InputField empNumbtxt;
    9.  
    10.     void Start()
    11.     {
    12.         empNumbtxt = GameObject.Find("EmpNumbButt").GetComponent<TMP_InputField>();
    13.     }
    14.  
    15.     void Update()
    16.     {  
    17.        ConvertAndPrint();
    18.     }
    19.  
    20.     public void ConvertAndPrint()
    21.     {
    22.         string x = empNumbtxt.text;
    23.         float empNumb = float.Parse(x);
    24.        
    25.         Debug.Log(empNumb * 2.5);
    26.     }
    27.  
    28. }
     
  2. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Well there are lots of ways to improve this seemingly short piece of code:
    1. Make empNumbtxt either public (instead of private) or add [SerializeField] so you can reference this TextMeshPro text within the inspector instead of using GameObject.Find (Lots of reason: 1. It is excruciatingly slow. 2. If you rename it you have to remember to change its name in the code - which you won't). You don't need your Start code then
    2. You can pass empNumbtxt.text directly into float.Parse instead of creating another variable
    3. Make sure to cache this value. Which is to mean that you only do this as and when you need it and you store it if you need to use it multiple times. You could do this with a mixture of hooking into the inputs on changed event and a property.
    Here is my example:

    Code (CSharp):
    1. using System;
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using TMPro;
    6.  
    7. public class EmpNumbButt : MonoBehaviour
    8. {
    9.     [SerializeField] TMP_InputField empNumbtxt;
    10.  
    11.     private void Update()
    12.     {
    13.        ConvertAndPrint();
    14.     }
    15.  
    16.     private void ConvertAndPrint()
    17.     {
    18.         float empNumb = float.Parse(empNumbtxt.text);
    19.  
    20.         Debug.Log(empNumb * 2.5);
    21.     }
    22. }
    As some extra information if you are planning on taking a project further down the line there are even more ways you can make your code more maintainable.

    A super simple one I like is the use of headers in the inspector. This allows you to almost categorise your variables in the inspector so it is easier to digest what you are looking at.

    I would also recommend looking at UnityEvents. These are how for example you hook up a function in your code to a button press, or the aforementioned on changed event for inputs. However you can also create, use and call these yourself for your own events.
     
    Last edited: Mar 7, 2022
    Acoustic125 likes this.
  3. Acoustic125

    Acoustic125

    Joined:
    Jan 20, 2022
    Posts:
    15
    Hey, really appreciate your time, and that def works. I did originally try it with a serialized or public field, but I was obviously missing something somewhere. I think I may have been trying to add an unnecessary call. And I've looking into the headers and event system for the last little while.

    As for the event system concept, on a basic level, is the general idea something like:

    You are creating a sort of single 'holder script' that can contain multiple events/functions, which would then allow you to apply/call those functions individually to a game object 'trigger' (in this case a button press), as opposed to otherwise having to potentially write or re-write multiple scripts that might. Something along those lines...?​
     
  4. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    It doesn't need to be a single script, the way I think of it is as follows. You have a script, lets say it manages the players health. You know that at some point, you will need to know when the player is damaged, this could be to update the UI for example. Instead of trying to call methods between your player, enemies and your UI, you could create an OnPlayerDamaged which could send over the amount of HP the player has after taking damage for example. Your UI script could then have a function/method which hooks into that event and simply updates a number in the UI for displaying the health. With an event, you do not need to care about who is listening for it you can just call it and if anyone is listening then they will get called.

    TL;DR - Way I think of it, use an event when you know you will need to trigger some functionality after an action has happened but you do not need to care about who is listening.

    Pseudo code:
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3.  
    4. public class Player : MonoBehaviour
    5. {
    6.     [Header("Variables")]
    7.     [SerializeField] int CurrentHealth = 100;
    8.  
    9.     [Header("Events")]
    10.     [SerializeField] IntEvent OnPlayerDamaged;
    11.  
    12.     public void TakeDamage(int amount)
    13.     {
    14.         CurrentHealth -= amount;
    15.  
    16.         OnPlayerDamaged.Invoke(CurrentHealth);
    17.     }
    18. }
    19.  
    20. // This just means that we are creating a UnityEvent we can use which will pass
    21. // through an integer (In this case our health to any listening functions)
    22. [System.Serializable]
    23. public class IntEvent : UnityEvent<int> {}
    24.  
    Code (CSharp):
    1. using System;
    2. using UnityEngine;
    3. using UnityEngine.UI;
    4.  
    5. public class UserInterface : MonoBehaviour
    6. {
    7.     // Assign this in the inspector
    8.     [SerializeField] Text myInterfaceText;
    9.  
    10.     // This function must take an integer parameter as defined in our IntEvent 'UnityEvent<int>'
    11.     // and will refer to our players current health. We can see that this script and function does
    12.     // not need any references to the player, this is handled through the event. All you
    13.     // need to do is drag and drop the GameObject this is on into the OnPlayerDamaged event and select
    14.     // this function in the dropdown (Similarly to the the button OnClick)
    15.     public void UpdatePlayerHealth(int health)
    16.     {
    17.         myInterfaceText.text = "Current Health: " + health.ToString();
    18.     }
    19. }
    20.  

    Whats nice about this is that it is fairly easy to extend. If you wanted to add an OnPlayerDied event you would simply add a new UnityEvent, by duplicating and renaming the '[SerializeField] UnityEvent OnPlayerDamaged;' line for example, adding an 'if(CurrentHealth <= 0) OnPlayerDamaged.Invoke(CurrentHealth)' to the TakeDamage function and now you can hook into that as well to for example, fade to black when the player dies.

    Hopefully you can see that with only a couple of dozen lines of code you can write what I think at least, is some pretty clean and useful code.
     
    Last edited: Mar 10, 2022