I’ve encountered a performance issue in my Unity project related to input processing within the FixedUpdate method. Currently, I’m attempting to read user input from the keyboard (to control my game) using the following code snippet:
void FixedUpdate()
{
if (Input.anyKey)
{
if (Input.inputString.Length != 0)
{
switch (Input.inputString[0])
{
// Cases handling specific input actions
}
}
}
}
However, I’ve noticed that employing Input.inputString[0] significantly slows down my code execution, making it impractical within FixedUpdate. As a workaround, I’ve resorted to a less efficient method:
void FixedUpdate()
{
if (Input.anyKey)
{
if (Input.GetKey(KeyCode.S))
{
// Handle input action for KeyCode.S
}
// Additional if statements for other key checks
}
}
This alternative approach is functional but not ideal (I have to manage all the different cases while in the first one I was able to get the ascii code of the input and manage everything in a more flexible way without writing code for every single letter).
I’m currently using Unity version 2022.3.12 with a fixed timestep of 0.025 (aiming for 40Hz).
Do anyone know why using Input.inputString[0] slows down my code so much? How can I solve this problem?
You should never, ever use the InputManager (the old input) in FixedUpdate. It leads to input loss. The input inside Unity is working in tandem to the Update method (once per frame), the FixedUpdate method runs zero to any times per frame. FixedUpdate is not really fixed, it is a “catch-up” method.
Also Input.inputString and Input.compositionInputString aren’t for handling keys. They are for handling string input. Which you aren’t doing. Use GetButton* and GetKey* methods for input.
Plus lose the if (Input.anyKey) too, it serves no purpose here. That is exceptionally slow.
But this whole thing depends on what you actually want to do, obviously, if you’re handling string input, then you just move to Update.
With the new Input System you can very straightforward use callback methods for each InputAction, meaning the code becomes completely oblivious to whether that action was caused by a mouse, keyboard, gamepad, joystick, touch or pen event.
But generally, switching over KeyCodes is to be expected if you want to hardcode keyboard input. Switching over strings (or char) has serious issues with locale. I always refer to the french keyboard layout to point out that you’ll make an angry french mob if you force them to use the actual WASD letters for movement.
That said if you don’t want to work with InputAction assets you can also do this. The example shows using a lambda but you could expand it to use a method to handle multiple keys for the same thing.
using System;
using System.Collections.Generic;
using UnityEngine.InputSystem;
public class KeyActions : MonoBehaviour
{
private Dictionary<KeyCode, Action> keyActions = new Dictionary<KeyCode, Action>();
void Start()
{
keyActions.Add(KeyCode.S, () =>
{
Debug.Log("Action for 'S' key executed.");
});
}
void FixedUpdate()
{
var pressedKeys = Keyboard.current.allKeys.Where(key => key.wasPressedThisFrame);
foreach (var key in pressedKeys)
{
if (keyActions.ContainsKey(key.keyCode))
{
keyActions[key.keyCode]();
}
}
}
}
Good to know! But …
I wouldn’t do that, that seems terribly inefficient just like Input.anyKey. This checks a property of more than 100 keys every time.
To be fair I wouldn’t do this maybe only for the simplest prototypes, but I think not even there. Running LINQs and/or unnecessary loops in any Update method, including FixedUpdate is a very bad idea if the performance of the application is any concern. There are simpler and more efficient ways to deal with actions and/or keypresses.
That’s not new input API. “.Where” is Linq expression, extension method that filters underlying sequence/container and returns you sequence based on lambda. So allKeys does give you all keys. “Where” also definitely allocates, for those who strive to achieve zero-allocation in C#.
Yeah, the ideal approach is to use the third workflow like I mentioned. It was more a lazy equivalent to the code that they were using which to be fair is likely worse. The allKeys field returns a ReadOnlyArray<KeyControl>.