콘솔의 기본적인 Console.ReadKey() 기반으로 입력을 받으면, OS의 연속키 입력 지연 때문에 입력이 느리고 답답하게 느껴진다.
이는 실시간 게임 같은 것에서는 조작감을 치명적으로 망쳐놓는 원인이 된다.
비슷한 현상을 Unity에서도 겪은 바 있기에, 이를 해결하고자 KeyDown / KeyUp을 이용한 인풋 시스템을 만들고자 했으나
C#에는 KeyUp 기능이 없다...!
없으면 직접 만들어야지 뭐...
결론부터 말하면 GetAsyncKeyState()라는 Win32 API가 제공하는 비동기 키 입력 감지 함수를 이용해 해결할 수 있다.
if (Console.KeyAvailable)
{
var key = Console.ReadKey(true).Key;
switch (key)
{
case ConsoleKey.LeftArrow: player.MoveLeft(); break;
case ConsoleKey.RightArrow: player.MoveRight(); break;
}
}
최초에는 기존 Console.ReadKey() 방식을 이용해 switch문으로 간단하게 실시간 입력을 처리했고, 위에서 언급한 키 입력 지연 현상을 겪었다.
이 문제를 해결하기 위해
Win32 API GetAsnycKetState를 이용해보자.
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
- vKey: 가상 키 코드 (예: 0x25 = ←, 0x26 = ↑ 등) // 폰트 오류... 티스토리 왜 그러니?
- 반환값의 의미:
- 0x8000 비트: 현재 키가 눌려있는지 (KeyDown 상태)
- 0x0001 비트: 직전에 눌린 적이 있는지 (KeyPressed 이벤트)
즉, 이 API를 이용하면 콘솔 프로그램에서도
게임 엔진처럼 KeyDown, KeyPressed, KeyUp을 구현할 수 있다
전체 코드는 아래와 같다.
using System;
using System.Runtime.InteropServices;
namespace WeaponMasterDefense
{
public static class InputSystem
{
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
public static void HandleInput(Player player)
{
if (IsKeyDown(ConsoleKey.Escape)) Program.SetPaused();
if (IsKeyDown(ConsoleKey.LeftArrow)) player.MoveLeft();
if (IsKeyDown(ConsoleKey.RightArrow)) player.MoveRight();
if (IsKeyDown(ConsoleKey.UpArrow)) player.MoveUp();
if (IsKeyDown(ConsoleKey.DownArrow)) player.MoveDown();
if (IsKeyPressed(ConsoleKey.Q)) player.skills[0].Activate(player);
if (IsKeyPressed(ConsoleKey.W)) player.skills[1].Activate(player);
if (IsKeyPressed(ConsoleKey.E)) player.skills[2].Activate(player);
if (IsKeyPressed(ConsoleKey.R)) player.skills[3].Activate(player);
}
public static bool IsKeyDown(ConsoleKey key)
{
return (GetAsyncKeyState((int)key) & 0x8000) != 0;
}
private static readonly bool[] prevState = new bool[256];
public static bool IsKeyPressed(ConsoleKey key)
{
int keyCode = (int)key;
bool isNowPressed = (GetAsyncKeyState(keyCode) & 0x8000) != 0;
bool pressed = isNowPressed && !prevState[keyCode];
prevState[keyCode] = isNowPressed;
return pressed;
}
public static void ClearKeyBuffer()
{
while (Console.KeyAvailable) Console.ReadKey(true);
}
}
}
0x8000 비트가 1이면 해당 키가 현재 눌려 있음을 의미한다.
매 프레임(루프)마다 검사하므로, 키를 꾹 누르고 있으면 계속 true가 반환된다.
키가 이전에 안 눌렸는데 지금 눌려있으면 KeyDown
지금 눌려있고 이전에도 눌려있으면 Hold
이전에 눌려있었는데 지금 안 눌려있으면 KeyUp
으로 구분한다.
이런식으로 OS의 연속 키 입력 지연과 상관없이 즉각적으로 반응하는 인풋시스템을 만들어 조작감 문제를 해결할 수 있다.
'C#' 카테고리의 다른 글
| Design Pattern - Adapter Pattern (어댑터 패턴) (0) | 2025.10.17 |
|---|---|
| C# - 콘솔로 "2중 버퍼링 (Double Buffering)" 구현하기 (0) | 2025.10.15 |
| C# - LockConsoleSize() : 콘솔 창 크기 고정 및 리사이즈 방지 (1) | 2025.10.14 |
| C# - 콘솔 폰트 크기 변경하기 SetCurrentConsoleFontEx (1) | 2025.10.13 |
| C# - 메모리 구조, GC (0) | 2025.10.13 |