Input Handling System Structure


Overview

The in-game input processing system consists of CInputManager and CInputComponent. It functions using the keyboard and mouse input states, along with the structures that bind these inputs. Each object binds the inputs it needs, and executes the corresponding callback functions in response to the inputs received.

The key advantage of this system is that it can handle multiple callback functions by combining various input conditions (input value + input state) for keyboard and mouse, allowing flexible design and extension of input configurations.


Input System Operating Structure | Full View

  • Execution order: InitializationUpdate
  • This diagram briefly illustrates the interaction of the input manager and input component.

InputUtils.h Descriptions | View Repository

This utility defines common types and structures needed for managing keyboard and mouse inputs, input states, and event bindings, which are essential for in-game input processing.

1. EKeyAction Enum Class

enum class EKeyAction : unsigned char
{
	PRESS,
	HOLD,
	RELEASE,
	MAX
};

  • Enum class used to distinguish different types of key inputs.

2. FKeyState Struct

struct FKeyState
{
	bool Press   = false;
	bool Hold    = false;
	bool Release = false;
};

  • Structure that stores the current input state of a single key.

3. FBindFunction Struct

struct FBindFunction
{
	void* obj = nullptr;
	std::function<void()> func;
};

  • Structure that stores a pair of object pointer and function (callback).

4. FBinder Struct

struct FBinder
{
	std::vector<std::pair<SDL_Scancode, EKeyAction>> Keys;
	std::vector<std::pair<Uint8, EKeyAction>> Mouses;
	
	std::vector<FBindFunction*> Functions;
};

  • Structure that combines keyboard and mouse inputs and links them to corresponding callback functions.

Class Descriptions

1. CInputManager Class | View Repository

class CInputManager
{
	friend class CEngine;
	
private:
	CInputManager();
	~CInputManager();
	
private:
	static CInputManager* mInst;
	
	// for keyboard
	std::unordered_map<SDL_Scancode, FKeyState> mKeys;
	
	// for mouse
	std::unordered_map<Uint8, FKeyState> mMouses;
	FVector2D mMousePos = FVector2D::ZERO;
	
private:
	bool Init();
	bool RegisterKey(SDL_Scancode keyCode);
	bool RegisterMouse(Uint8 button);
	
	void Update();
	void UpdateInputState();
	void HandleInputState(bool& press, bool& hold, bool& release, bool isPressed);
	
public:
	bool GetKeyState(SDL_Scancode keyCode, EKeyAction action);
	bool GetMouseButtonState(Uint8 button, EKeyAction action);
	const FVector2D& GetMousePos() const { return mMousePos; }
	
public:
	static CInputManager* GetInst()
	{
		if (!mInst)
			mInst = new CInputManager;
		return mInst;
	}
	
	static void DestroyInst()
	{
		SAFE_DELETE(mInst);
	}
};


2. CInputComponent Class | View Repository

class CInputComponent : public CComponent
{
public:
	CInputComponent();
	virtual ~CInputComponent();
	
private:
	std::unordered_map<std::string, FBinder*> mBinders;
	
private:
	virtual void Update(float deltaTime) final;
	virtual void Release() final;
	
public:
	template <typename T>
	void AddFuncToBinder(const std::string& key, T* obj, void(T::* func)());
	void AddFuncToBinder(const std::string& key, void* obj, const std::function<void()>& func);
	
	void DeleteFuncFromBinder(const std::string& key, void* obj);
	
	void AddInputToBinder(const std::string& key, SDL_Scancode keyCode, EKeyAction action);
	void AddInputToBinder(const std::string& key, Uint8 button, EKeyAction action);
};


Conclusion

Through this implementation, I learned that separating the CInputManager and CInputComponent allows for clean handling of custom key mappings, various input patterns, and complex input conditions. Additionally, it provided a deep understanding of how to design the input system in a flexible and extensible manner.