Skip to content
HY Devlog
Go back

Why Enter Does Nothing in an InputField: The Condition Reads a Cache

I clipped this while adding login to a game project — building the UI that takes a nickname and a password. I was also looking for the output side, how to lay a ranking board on screen, and this post happens to join the two: it takes a name and saves it, and its sequel builds a ranking out of that value.

It’s a 2021 post on taking a player name with UGUI’s InputField. It walks through the input field’s structure, then adds a background image and a confirm button to build an actual name-entry window. The author states why the code exists:

At that point the player types the name on the keyboard and then has to move their hand back to the mouse..! I didn’t want that, so I wrote a script where both pressing the button on the keyboard and clicking the input button with the mouse would work.

Clear goal. But the keyboard path in that code does nothing until the mouse button has been clicked once. Code written to avoid reaching for the mouse requires reaching for the mouse exactly once to unlock.

Table of Contents

Table of Contents

The Input Field’s Structure Is Described Correctly

Start with what’s right. The clipping explains why the input field is so simple:

because it really doesn’t include any functionality beyond displaying the entered value

A fair observation. Creating an InputField object gives you two children, Placeholder and Text, and the manual says the same thing the clipping does.

Placeholder — An optional “empty” Graphic to show that the Input Field is empty of text.

Text — drag the object to the Input Field’s Text property to enable editing. The Text property of the Text control itself will change as the user types.

“Optional” is the documentation’s own word for Placeholder. Which makes the clipping’s next piece of advice correct too.

It isn’t a required element, so deleting it won’t cause any disaster. To avoid a possible reference error, clear the Placeholder value on the parent InputField object’s input field component so it becomes None.

Pointing out that you should also clear the component’s reference is a good catch.

One addition: in current Unity this component’s menu path is UI (Canvas)/Legacy/Input Field. It sits under Legacy in the component menu. For a new project, TextMeshPro’s TMP_InputField is the default choice. The clipping does note “you can also choose depending on whether you use TMP”, but the weight of that choice has shifted since.

Enter Never Works, Not Once

The clipping’s code:

using UnityEngine;
using UnityEngine.UI;

public class ResultNameInput : MonoBehaviour
{
    public InputField playerNameInput;
    private string playerName = null;

    private void Awake()
    {
        playerName = playerNameInput.GetComponent<InputField>().text;
    }

    private void Update()
    {
        //키보드
        if (playerName.Length > 0 && Input.GetKeyDown(KeyCode.Return))
        {
            InputName();
        }
    }

    //마우스
    public void InputName()
    {
        playerName = playerNameInput.text;
        PlayerPrefs.SetString("CurrentPlayerName", playerName);
        GameManager.instance.ScoreSet(GameManager.instance.score, playerName);
    }
}

Just follow where playerName changes. Two places.

Typing does not change playerName. Typing changes the input field’s text; playerName is a cache that copied that value once, at Awake.

And the condition in Update reads that cache.

StepInputField.textplayerNamePressing Enter
Start""""Condition false — nothing
Type “HY”"HY"""Condition false — nothing
Click confirm with mouse"HY""HY"— (the button runs it directly)
From then on"HY…""HY"Condition true — it works

Row three has to happen before row two opens. The keyboard path only comes alive after the mouse has clicked the button once. The very action the author set out to remove is a precondition.

Here’s how the author read that condition:

it decides “ah, they’ve finished entering” based on one or more characters of what is presumably a name, plus an Enter press. If nothing has been entered it doesn’t run.

“If nothing has been entered it doesn’t run” is a correct outcome. The reason is different, though. The intent is “did they type at least one character?”, while what’s actually tested is “has InputName ever run?”

Two Ways to Fix It

Read the Current Value Instead of the Cache

The minimal fix is one line. Have the condition look at the input field itself, not the cache.

private void Update()
{
    if (_playerNameInput.text.Length > 0 && Input.GetKeyDown(KeyCode.Return))
    {
        Submit();
    }
}

Once you do that, the playerName field has no reason to exist for the test. Keeping the state in two places was the cause, so delete the field and read .text when you need it.

One documented nuance is worth knowing here.

text — Input field’s current text value. This is not necessarily the same as what is visible on screen.

Set contentType to Password and the screen shows * while text is the real string. It’s an API that reads the value, not what is shown.

If you’re building a password field, that distinction matters. Password hides the screen only; the value sits in memory as plain text. It’s a display setting, not a security setting.

onEndEdit Instead of Update

Rather than checking a key every frame, you can let the input field tell you. From the manual:

On End Edit — A UnityEvent that is invoked when the user finishes editing the text content either by submitting or by clicking somewhere that removes the focus.

Wire a method to On End Edit in the Inspector, or attach it in code.

private void OnEnable()
{
    _playerNameInput.onEndEdit.AddListener(OnEndEdit);
}

private void OnDisable()
{
    _playerNameInput.onEndEdit.RemoveListener(OnEndEdit);
}

private void OnEndEdit(string value)
{
    // The confirmed string arrives as the argument. No cache to consult.
    if (value.Length == 0) { return; }
    Submit();
}

But exactly as quoted, this event also fires when focus is lost. Clicking elsewhere submits too, so if the goal is “submit only on Enter”, this alone isn’t it. The API reference also lists onSubmit, but its description is worded identically to onEndEdit, so I won’t claim a difference the docs don’t draw.

That’s why this post’s example keeps the Enter check in Update and reads the value straight from .text. Within what the documentation confirms, that’s the closest thing to the original’s intent.

Input.GetKeyDown Throws Under the Input System

The original uses Input.GetKeyDown(KeyCode.Return). Depending on project settings, that becomes a runtime exception. The message, as filed on Unity’s Issue Tracker:

InvalidOperationException: You are trying to read Input using the
UnityEngine.Input class, but you have switched active Input handling to
Input System package in Player Settings.

If Active Input Handling in Player Settings is Input System Package (New), the old Input class throws this. To use both, set it to Both.

On a project that has moved to the Input System, the key check belongs there too.

using UnityEngine.InputSystem;

if (Keyboard.current != null && Keyboard.current.enterKey.wasPressedThisFrame)
{
    Submit();
}

And whether you stay on the old Input or not, watching only KeyCode.Return misses the numpad Enter. They are separate values.

Return — Return key.

KeypadEnter — Numeric keypad Enter.

Where and Why You’d Use It

A name-entry window is a UI whose whole job is deciding when “they’re done.” To keep both the keyboard and the mouse open, that decision has to live in one place.

One Name-Entry Window

using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Takes a name in an input field and confirms it. Supports both Enter and the confirm button.
/// </summary>
public class NameEntryPanel : MonoBehaviour
{
    private const int NAME_LIMIT = 12;
    private const string DEFAULT_NAME = "NONAME";

    [Header("References")]
    [SerializeField, Tooltip("The input field that takes the name")]
    private InputField _playerNameInput;

    [SerializeField, Tooltip("Confirm button. Disabled while the field is empty.")]
    private Button _confirmButton;

    private void Awake()
    {
        // Don't cache text in Awake. At that point the value is always empty.
        _playerNameInput.characterLimit = NAME_LIMIT;
    }

    private void OnEnable()
    {
        _playerNameInput.onValueChanged.AddListener(OnValueChanged);
        OnValueChanged(_playerNameInput.text);

        // Let the player type the moment the window opens.
        _playerNameInput.ActivateInputField();
    }

    private void OnDisable()
    {
        _playerNameInput.onValueChanged.RemoveListener(OnValueChanged);
    }

    private void Update()
    {
        if (!IsSubmitPressed()) { return; }

        // Read the input field's current value, not a cache.
        if (_playerNameInput.text.Length == 0) { return; }

        Submit();
    }

    /// <summary>Hook this up to the confirm button's OnClick.</summary>
    public void Submit()
    {
        string typed = _playerNameInput.text.Trim();
        string playerName = typed.Length > 0 ? typed : DEFAULT_NAME;

        PlayerPrefs.SetString("CurrentPlayerName", playerName);
        // Write explicitly if a scene change is about to happen.
        PlayerPrefs.Save();

        // Leave what follows (ranking, scene transition) to the caller.
    }

    private void OnValueChanged(string value)
    {
        // Lock the button while empty. The state becomes visible, which makes debugging easy.
        if (_confirmButton != null)
        {
            _confirmButton.interactable = value.Trim().Length > 0;
        }
    }

    private static bool IsSubmitPressed()
    {
        // The numpad Enter is a separate KeyCode.
        return Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.KeypadEnter);
    }
}

A few intentions worth noting.

What I Changed from the Original

Where Not to Use It

Unity stores PlayerPrefs in a local registry, without encryption. Don’t use PlayerPrefs data to store sensitive data.

Wrapping Up

The original’s motivation was right. “I don’t want to move my hand from the keyboard to the mouse” really is the most annoying thing about a name-entry window, and opening both paths was the correct design. What went wrong wasn’t the design — it was one line deciding where the value is read from.


References

The starting point for this post was 김시루시루르 — [Unity UGUI] InputField 인풋 필드 + α (플레이어 이름 입력) (2021-12-06). I followed its explanation of the input field’s structure and its sample code as written, traced the code’s execution order, and checked each API’s behavior against the current documentation. Quotes from it are my translations; the Korean comments in the sample are the author’s.


Share this post:

Previous Post
PlayerPrefs' Path Is Assembled from Company Name and Product Name
Next Post
There Is No Signaling in the Unity WebRTC Sample