Is it worth it to abstract out your input?

I’m currently working on abstracting out input gathering into its own class that then calls on the other classes methods where input is needed. Is this something that is wise to do?

using System.Collections;
using UnityEngine;

public class InputCapture : MonoBehaviour
{
    private CameraRotate camRotate;
    private PlayerMovement playerMovement;
    private WeaponBase weapon;
    private WeaponListController weaponList;

    private void Start()
    {
        this.camRotate = Camera.main.GetComponent<CameraRotate>();
        this.playerMovement = GetComponent<PlayerMovement>();
        this.weapon = GetComponentInChildren<WeaponBase>();
        this.weaponList = GetComponentInChildren<WeaponListController>();
    }

    private void FixedUpdate()
    {
        #region Movement Input

        //******************************************************************/
        // Get input from the player and move FOR/BACK/LEFT/RIGHT depending
        // on the keys pressed. 
        //******************************************************************/
        float horzDir = Input.GetAxisRaw("Horizontal");
        float forBackDir = Input.GetAxisRaw("Vertical");
        if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
            this.playerMovement.Move(horzDir, forBackDir);

        #endregion
    }

    private void Update()
    {
        #region Camera Rotation

        if (Input.GetKeyDown(KeyCode.E))
            this.camRotate.HandleInput(KeyCode.E);
        if (Input.GetKeyDown(KeyCode.Q))
            this.camRotate.HandleInput(KeyCode.Q);

        #endregion

        #region Shoot Weapon

        if (Input.GetButton("Fire1"))
        {
            weapon.Shoot();
        }
        if (Input.GetButtonUp("Fire1"))
        {
            weapon.WeaponEffectOff();
        }

        #endregion

        if (Input.GetKeyDown(KeyCode.Alpha1))
            this.weaponList.SelectNextWeapon();
    }
}

by all means have a separate function to handle all input (keyboard, controllers, touch, network, etc.) to set the state of each input device, but it's generally used by a player controller script and shouldn't directly call specific behaviours/actions - that's the player controller's job! i'm confused as to why you've made coroutines to do it, when you could easily handle in Update()/FixedUpdate()/etc.

It's generally smart to have a single class that handles all input. That makes it a lot easier to do things like redirecting input (when you are in a menu), preventing input (during cutscenes such) and so on. It should send it's input to some other controller class, and when you need to send input somewhere else, you just swap out what controller receives input from the input controller. Super easy. As Zionmoose says, there's no real need to have the input handling happen in a coroutine, though, at least not the input handling you're doing.

The coroutine was practice and I realize that. I will be changing the code to update and fixed update.

Code updated to not use coroutines.

1 Answer

1

Have your input class to set a list of event to be called based on the state of the input. Then have a component to do the link between your input and the class to perform action then finally have your class.

public class InputSystem:MonoBehaviour{
    public delegate void OnMousePressDown(Vector3 position);
    public static event OnMousePressDown OnMousePress = delegate{};

    void Update(){
       if(Input.getMouseButtonDown(0)){
           OnMousePress(Input.mousePosition);
       }
    }
}

This above does not need to be a MonoBehaviour nor the event to be static but this is the fastest way for now.

public class InputObjectController:MonoBehaviour{
    [SerializeField] private ObjectScript objectScript;

    public EnableControl(){
        InputSystem.OnMousePress += objectScript.MethodToCall;
    }

    public DisableControl(){
        InputSystem.OnMousePress -= objectScript.MethodToCall;
    }
} 

public class ObjectScript:MonoBehaviour{
    public void MethodToCall(Vector3 position){}
}

it might sound like a waste but the purpose is that your ObjectScript is oblivious and independant to the InputSystem and still can benefit from it. Nonetheless, you could skip the middle part and put it all inside the ObjectScript but that would give more responsibility to the class and it is always better to narrow the tasks to only the ones needed and create as many classes as tasks.

Also, my InputSystem does not care about the classes that will use it while in your case, it seems your input class can only be used in this project.

Still fairly new to coding, haven't used delegates yet. I will have to research what is happening in your code.

Found this which actually explains how this works. Excellent. Thanks! http://www.indiedb.com/groups/unity-devs/tutorials/delegates-events-and-singletons-with-unity3d-c