대규모 게임의 입력 관리 기술


16

대형 게임에서 입력을 관리하는 표준 기술이 있습니까? 현재 내 프로젝트에서 모든 입력 처리는 게임 루프에서 다음과 같이 수행됩니다.

while(SDL_PollEvent(&event)){
            switch(event.type){
                case SDL_QUIT:
                    exit = 1;
                    break;
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym){
                        case SDLK_c:
                            //do stuff
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    switch(event.button.button){
                        case SDL_BUTTON_MIDDLE:
                                //do stuff
                                break;
                            }
                    }
                    break;
            }

(SDL을 사용하고 있지만 주요 사례는 라이브러리와 프레임 워크도 적용한다고 생각합니다). 큰 프로젝트의 경우 이것이 최선의 해결책이 아닌 것 같습니다. 사용자가 무엇을 눌렀는지 알고 싶은 객체가 여러 개있을 수 있으므로 해당 객체가 입력을 처리하는 것이 더 합리적입니다. 그러나 이벤트를 가져온 후 이벤트 버퍼에서 푸시되므로 다른 객체가 해당 입력을 수신하지 않으므로 입력을 모두 처리 할 수는 없습니다. 이를 방지하기 위해 가장 일반적으로 사용되는 방법은 무엇입니까?


이벤트 관리자를 사용하면 이벤트를 입력하여 게임의 다른 모든 부분을 등록 할 수 있습니다.
danijar

@danijar 이벤트 관리자가 정확히 무엇을 의미하는지, 어떤 종류의 이야기를하고 있는지 보여주기 위해 골격 의사 코드를 제공 할 수 있다면 가능합니까?
w4etwetewtwet


1
이벤트 관리자를 정교하게 작성하기위한 답변을 썼습니다.
danijar

답변:


12

스레드 스타터가 요청한 이후 이벤트 관리자에 대해 자세히 설명합니다. 이것이 게임에서 입력을 처리하는 좋은 방법이라고 생각합니다.

이벤트 관리자는 콜백 함수를 키에 등록하고 해당 콜백을 발생시키는 글로벌 클래스입니다. 이벤트 관리자는 등록 된 기능을 키별로 그룹화 된 개인 목록에 저장합니다. 키가 실행될 때마다 등록 된 모든 콜백이 실행됩니다.

콜백은 std::function람다를 담을 수있는 객체 일 수 있습니다 . 키는 문자열 일 수 있습니다. 관리자는 전역 적이므로 응용 프로그램의 구성 요소는 다른 구성 요소에서 시작된 키에 등록 할 수 있습니다.

// in character controller
// at initialization time
Events->Register("Jump", [=]{
    // perform the movement
});

// in input controller
// inside the game loop
// note that I took the code structure from the question
case SDL_KEYDOWN:
    switch(event.key.keysym.sym) {
    case SDLK_c:
        Events->Fire("Jump");
        break;
    }
    break;

값을 추가 인수로 전달할 수 있도록이 이벤트 관리자를 확장 할 수도 있습니다. C ++ 템플릿은이 기능에 적합합니다. 예를 들어, "WindowResize"이벤트를 위해 새 창 크기를 전달 하기 위해 이러한 시스템을 사용할 수 있으므로 청취 구성 요소가 직접 가져올 필요가 없습니다. 이것은 코드 의존성을 상당히 줄일 수 있습니다.

Events->Register<int>("LevelUp", [=](int NewLevel){ ... });

내 게임에 이벤트 관리자를 구현했습니다. 관심이 있으시면 여기에 코드 링크를 게시하겠습니다.

이벤트 관리자를 사용하면 애플리케이션 내에서 입력 정보를 쉽게 브로드 캐스트 할 수 있습니다. 또한 이것은 사용자가 키 바인딩을 사용자 정의 할 수있는 좋은 방법입니다. 컴포넌트는 키 대신 시맨틱 이벤트를 직접 청취합니다 ( "PlayerJump"대신 "KeyPressedSpace"). 그런 다음 "KeyPressedSpace"사용자가 해당 키에 바인딩 한 작업 을 수신 하고 트리거 하는 입력 매핑 구성 요소를 가질 수 있습니다 .


4
좋은 답변, 감사합니다. 코드를보고 싶지만 복사하고 싶지 않으므로 직접 구현할 때까지 게시하지 않아도됩니다.
w4etwetewtwet

난 그냥 이런 식으로 모든 멤버 함수를 전달할 수 있습니다, 뭔가 생각, 또는 레지스터 기능은 하나의 클래스 멤버 함수에 제한 AClass : FUNC를 취할 필요가 없습니다
w4etwetewtwet

그것은 C ++의 람다 식에 대한 좋은 점입니다. 캡처 절을 지정할 수 [=]있으며 람다에서 액세스 한 모든 로컬 변수에 대한 참조가 복사됩니다. 따라서이 포인터 나 이와 비슷한 것을 전달할 필요가 없습니다. 그러나 오래된 C 함수 포인터에는 캡처 절이있는 람다를 저장할 수 없습니다 . 그러나 C ++ std::function은 제대로 작동합니다.
danijar

std :: function 속도가 매우 느림
TheStatehz

22

이것을 여러 층으로 나눕니다.

가장 낮은 계층에는 OS의 원시 입력 이벤트가 있습니다. SDL 키보드 입력, 마우스 입력, 조이스틱 입력 등. 여러 플랫폼이있을 수 있습니다 (SDL은 나중에 입력해야 할 여러 입력 형식이없는 최소 공통 분모입니다).

"키보드 버튼 누름"등과 같은 매우 낮은 수준의 사용자 정의 이벤트 유형으로이를 추상화 할 수 있습니다. 플랫폼 계층 (SDL 게임 루프)이 입력을 받으면 이러한 하위 레벨 이벤트를 작성한 다음 입력 관리자에게 전달해야합니다. 간단한 메소드 호출, 콜백 함수, 복잡한 이벤트 시스템 등 원하는 방식으로이를 수행 할 수 있습니다.

입력 시스템은 이제 하위 레벨 입력을 상위 레벨 논리 이벤트로 변환하는 작업을 수행합니다. 게임 로직은 SPACE를 눌렀을 때 전혀 신경 쓰지 않습니다. JUMP를 누르는 것이 중요합니다. 입력 관리자의 임무는 이러한 낮은 수준의 입력 이벤트를 수집하고 높은 수준의 입력 이벤트를 생성하는 것입니다. 스페이스 바와 'A'게임 패드 버튼이 모두 논리적 명령 Jump에 매핑된다는 것을 아는 것은 책임입니다. 게임 패드 대 마우스 모양 컨트롤 등을 처리합니다. 낮은 수준의 컨트롤에서 가능한 추상적 인 높은 수준의 논리 이벤트를 생성합니다 (여기에는 몇 가지 제한 사항이 있지만 일반적인 경우에는 완전히 추상화 할 수 있습니다).

캐릭터 컨트롤러는 이러한 이벤트를 수신하고 이러한 높은 수준의 입력 이벤트를 처리하여 실제로 응답합니다. 플랫폼 계층에서 "키 다운 스페이스 바"이벤트를 보냈습니다. 입력 시스템이이를 수신하여 맵핑 테이블 / 논리를보고 "Pressed jump"이벤트를 보냅니다. 게임 로직 / 캐릭터 컨트롤러는 해당 이벤트를 수신하고 플레이어가 실제로 점프 할 수 있는지 확인한 다음 "플레이어 점프"이벤트를 발생시킵니다 (또는 점프가 직접 발생 함). .

게임 로직에 의존하는 것은 플레이어 컨트롤러에 들어갑니다. OS에 의존하는 것은 플랫폼 계층에 있습니다. 나머지는 모두 입력 관리 계층으로 들어갑니다.

다음은 이것을 설명하는 아마추어 ASCII 아트입니다.

-----------------------------------------------------------------------
Platform Abstraction | Collect and forward OS input events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
    Input Manager    | Translate OS input events into logical events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
Character Controller | React to logical events and affect game play
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
      Game Logic     | React to player actions and provides feedback
-----------------------------------------------------------------------

멋진 ASCII 아트이지만 필요하지는 않지만 죄송합니다. 대신 번호 매기기 목록을 사용하는 것이 좋습니다. 어쨌든 좋은 대답!
danijar

1
@ danijar : 어, 나는 실험하고 있었고, 전에 답을 그리려고 시도하지 않았습니다. 가치있는 것보다 더 많은 작업이 필요하지만 페인트 프로그램을 다루는 것보다 훨씬 적은 작업입니다. :)
Sean Middleditch

좋아, 이해
할만한

8
개인적으로, 나는 지루한 번호 목록보다 ASCII 아트 방식을 선호합니다.
Jesse Emond

@JesseEmond Hey, 여기 예술?
danijar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.