Skip to content

Input Containers Quick Start Guide

Koopa1018 edited this page Jan 2, 2021 · 2 revisions

In order to use the Input Containers package, do the following:

Create your input fields

A field is a small component which stores input. (These are the "containers" the title talks about--but I had to leave'm with the old name because my existing projects already call 'em fields, so I couldn't change it without breaking it!)

Because every game is different, this package does not provide any pre-built fields. Instead, it provides three base classes you can inherit from to create fields that your game needs. These base classes are ScalarInputField, AxisInputField and ButtonInputField. ScalarInputField stores a floating-point number, or "scalar value" (e.g. 0.1f); AxisInputField stores two floating-point numbers, a 2D vector if you prefer (e.g. (0.5f,-1)); ButtonInputField stores a boolean value (e.g. true).

An example ScalarInputField-based field:

using Clouds.PlayerInput;

namespace Example.PlayerInput {
    public class WalkInputField : ScalarInputField {
        //This page intentionally left blank, ba-dum-tish.
    }
}

An example AxisInputField-based field:

using Clouds.PlayerInput;

namespace Example.PlayerInput {
    public class AimInputField : AxisInputField {
        //This page intentionally left blank, ba-dum-tish.
    }
}

An example ButtonInputField-based field:

using Clouds.PlayerInput;

namespace Example.PlayerInput {
    public class JumpInputField : ButtonInputField {
        //This page intentionally left blank, ba-dum-tish.
    }
}

Yes, these are all basically the same--in fact, this is what all your input field components are going to look like. You can add fields and methods if you'd like; it's best to just treat these like numbers to be read from and written to, so you probably shouldn't (unless you've got something really funky going on--and I'd love to see it if you are, it sounds really fascinating~), but it won't hurt anything (code-wise) if you do.

Create a component to write your fields

This package comes with a simple (primitive, really) component which can write a single input into a single button, PlayerButtonInput. As useful as this component can be, though, it'd be extremely impractical to use this one single component for all the myriad inputs, player and AI, you need to get each frame (even if you do use it for some of them--no shame in that, and actually it's built for prototyping~). In other words, you're going to have to write your input-generator component yourself.

To create a field-writing component, create a component implementing IGenerateInputSignals. IGenerateInputSignals requires two void methods, GenerateInputSignal() and ClearInputSignal(). These are hopefully fairly self explanatory: in GenerateInputSignal(), you set the input fields, and in ClearInputSignal(), you unset them, or set them to what is essentially neutral. If it helps, visualize input fields as individual wires, with current that can be as high or low as you need it.

These are effectively the Update() methods of Input Containers; to keep things sorted out in your own code, it'd be a good idea not to leave any of Unity's own update functions in the class (that'd be Awake(), Start(), Update(), FixedUpdate(), etc.).

Please bear in mind: these methods can be literally anything you choose. You can set your components using Unity's old input system, Unity's new input system, or ReWired*; you can set them with input from the net, or from a replay file; you can write the exact same input on every frame, or decide what inputs to give based on a decision tree, or, you know, get them from another component!~

(*DISCLAIMER: I've never used ReWired)

An example IGenerateInputSignals component (using Unity's old input system):

using UnityEngine;
using Clouds.PlayerInput;

namespace Example.PlayerInput { //do not name your namespace "Input," it'll screw things up when you use Unity's Input system

    public class ExampleInputGetter : MonoBehaviour, IGenerateInputSignals {
        //Add references to the components you want to write to
        [SerializeField] WalkInputField walkInput;
        [SerializeField] JumpInputField jumpInput;

        public void GenerateInputSignal () {
            //Here's where you decide what inputs to send down the pipeline.

            //For demonstration purposes, I'm going to use Unity's old input system.
            walkInput.Value = Input.GetAxis("Horizontal");
            jumpInput.Value = Input.GetButton("Jump");
        }

        public void ClearInputSignal () {
            //Here you should set your input fields essentially to neutral.
            walkInput.Value = 0;
            jumpInput.Value = false;
        }

    }
}

Add your generator next to an InputGetter component

InputGetter is essentially the starting point into the input system. When the scene begins, it detects IGenerateInputSignals components that are next to it in the GameObject; then, in every Update, it calls GenerateInputSignal() on that IGenerateInputSignals. Put it on the same GameObject as you put the IGenerateInputSignal().

When disabled, InputGetter will call ClearInputSignal() to revert the object to a neutral position. If you disable the IGenerateInputSignals component, input will not revert--the last input will remain hanging until you re-enable the component, unless you program it otherwise (because, after all, it is your code). This behavior is by design--suppose you want a character to keep walking after they touch the exit?

Link the input fields into what they're needed for

Except in the cases of interfaces (like Input Getter and IGenerateInputSignals), I usually like to tie components together using editor-exposed fields rather than by detecting them at runtime using GetComponent<T>(). This means you're going to have to drag and drop the input fields (or the object they're on) into the selection boxes, or click the boxes' radio buttons(?) and use the asset browser that opens up. For this reason, I usually put all my input fields together with InputGetter and my generator on a sub-object of the character, which I call the "Input Nucleus." It's also more organized. (Not gonna lie to you--using this system, you're going to end up with a lot o'components acting on your GameObjects.)

And that's it.

TODO: Write outro.