입력 처리 시스템 구조


개요

게임 내 입력을 관리하는 입력 처리 시스템은 CInputManagerCInputComponent로 구성되며, 키보드/마우스 입력 상태와 이를 바인딩하는 구조체를 통해 동작합니다. 각 오브젝트는 자신에게 필요한 입력을 바인딩하고, 발생한 입력에 따라 연결된 콜백 함수를 실행합니다.

이 시스템의 주요 장점은 여러 콜백 함수에 대해 키보드와 마우스의 다양한 입력 조건(입력값 + 입력상태)을 조합해 처리할 수 있어, 입력 방식을 유연하게 설계하고 확장할 수 있다는 점입니다.


입력 시스템 작동 구조 | 자세히 보기

  • 실행 순서: InitializationUpdate
  • 본 설계도는 입력 매니저(CInputManager)와 입력 컴포넌트(CInputComponent) 두 클래스 간의 연계성을 간단히 보여줍니다.

InputUtils.h 구성 요소 및 설명 | 저장소 보기

키보드와 마우스의 입력값, 입력상태, 이벤트 바인딩을 관리하며, 게임 내 입력 처리를 위해 필요한 공통 자료형과 구조체들을 정의하는 유틸 항목입니다.

1. EKeyAction 열거형 클래스

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

  • 키 입력타입을 구분하기 위한 열거형 클래스입니다.

2. FKeyState 구조체

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

  • 단일 키의 현재 입력상태를 저장하는 구조체입니다.

3. FBindFunction 구조체

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

  • 오브젝트 포인터와 함수(콜백)을 한 쌍으로 보관하는 구조체입니다.

4. FBinder 구조체

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

  • 키보드와 마우스 입력을 조합하고, 해당 입력에 대한 동작(콜백 함수)을 함께 묶어주는 구조체입니다.

클래스 목록 및 설명

1. CInputManager 클래스 | 저장소 보기

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 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);
};


맺는 말

이번 구현을 통해 입력 매니저(CInputManager)와 입력 컴포넌트(CInputComponent)를 분리하여 관리하면, 사용자 정의 키 매핑이나 다양한 입력 패턴, 복잡한 입력 조건깔끔하게 처리할 수 있다는 점을 배웠습니다. 또한, 유연하고 확장성 있게 설계하는 관점에서 입력 시스템을 구성하는 방법을 깊이 이해하게 되었습니다.