Dev Tips #1: Buffered Inputs

  • 2023-04-23

    2023-05-21

    Sebastian Sela

  • buffered inputs

    dev tip

    game dev

    programming

    unity

Read more

Hey all, I feel like writing a tip for those of you who code games. Writing this one as a blog post, but I'm thinking of creating a new section on the site for these, at least if I write more than one.

Anyway, I'm here today to share an implementation for buffered button inputs I've written using Unity. It's a relatively simple solution and provides an indiviual buffer for any input you might have. This works great for e.g. allowing the player to jump when landing after having pressed the jump button slightly before landing on the ground. Check the end for the full script.

To start, create an enum containing all your planned input types. Keep this enum updated with all the new inputs you add. If you're using Unity's new input system package, you can have a look there to see all your inputs and create the enum from the values you see there. Check out this video for an explanation on the new input system package. For this example, let's assume we have the inputs Jump and Fire1.

public enum InputType { Jump, Fire1 }

With the enum in place, let's create a float array. This will keep track of our timers. Place it in the script where the inputs will be read.

float[] bufferArray;

Let's also create a variable for the time we want our buffer to last. For this example, let's use half a second.

float bufferDuration = 0.5f;

Currently, the array is useless. We want to initialize it so that it has a value for each of the inputs we have. Let's create a function for that. Using the enum we created, we have acces to the amount of input we have. Create the array by using the length of the enum; use Enum.GetValues to get its length.

float[] InitializeBuffer() { Type enumType = typeof(InputType); int enumLength = Enum.GetValues(enumType).Length; float[] floatArray = new float[enumLength]; return floatArray; }

Now we have a function to initialize the array. Do so in the Start or Awake function of the input-checking script. Let's also add a debug statement to check how long the array is.

void Start() { bufferArray = InitializeBuffer(); Debug.Log(bufferArray.Length); }

If done correctly, the debug statement should print out a 2: one for Jump, and one for Fire1. Now that we have the array, let's make it a timer. In FixedUpdate, let's write a for-loop, so that for each value in the array, reduce its value by Time.deltaTime if it's value is above 0.

void FixedUpdate() { for(int i = 0; i < bufferArray.Length; i++) { if(bufferArray[i] < 0) { bufferArray[i] -= Time.deltaTime; } } }

Great, the timer is in place! And with the array initialized, there's just one more thing to do: start the buffer. How do we do this? Simple: Set the specified buffer to its duration variable. We can specify which position by using the enum. Get the specified enum type, and read its int value.

void Update() { if(Input.GetButtonDown(InputType.Fire1.ToString())) { bufferArray[(int)InputType.Fire1] = bufferDuration; } }

Now the value placed at index (int)InputType.Fire1 will have a value. This value will go down by Time.deltaTime each time FixedUpdate is run. To use it, we can simply check if the bufferArray's value is above 0. If yes, do action. Don't forget to reset the buffer's value to 0 when the action is done!

void Update() { if(Input.GetButtonDown(InputType.Fire1.ToString())) { bufferArray[(int)InputType.Fire1] = bufferDuration; } if(bufferArray[(int)InputType.Fire1] > 0) { Debug.Log($"{InputType.Fire1.ToString()} pressed"); bufferArray[(int)InputType.Fire1] = 0; } }

That's it, the buffer system is complete! Of course, this code is written with Unity's input system in mind, but with a few changes you can probably get this working with your desired system.

Buffered inputs are great to have to make your game feel less sticky, so use it well. If you add a bool to the "if buffer > 0" statement, e.g. a ground check bool, then if that bool switches to true and the buffer value is still over 0, the desired action will happen, even if no new input has been made. The code in this post can of course be further optimized, but I kept it somewhat detailed for the sake of clarity. Oh, and if there are any errors, please let me know. Finally, a full script for you to copy/paste.

using UnityEngine; public class BufferedInputs : MonoBehaviour { float[] bufferArray; float bufferDuration = 0.5f; public enum InputType { Jump, Fire1 } void Start() { bufferArray = InitializeBuffer(); Debug.Log(bufferArray.Length); } void FixedUpdate() { for(int i = 0; i < bufferArray.Length; i++) { if(bufferArray[i] < 0) { bufferArray[i] -= Time.deltaTime; }f } } void Update() { if(Input.GetButtonDown(InputType.Fire1.ToString()) { bufferArray[(int)InputType.Fire1] = bufferDuration; } if(bufferArray[(int)InputType.Fire1] > 0) { Debug.Log("Fire1 pressed"); bufferArray[(int)InputType.Fire1] = 0; } } float[] InitializeBuffer() { Type enumType = typeof(InputType); int enumLength = Enum.GetValues(enumType).Length; float[] floatArray = new float[enumLength]; return floatArray; } }
Read more