게임 엔진에서 이벤트 중심 커뮤니케이션 : 예 또는 아니오?


62

Game Coding Complete를 읽고 있으며 저자는 게임 객체와 모듈 간의 이벤트 기반 통신을 권장 합니다.

기본적으로 모든 살아있는 게임 행위자는 내부 이벤트 메시징 시스템을 통해 주요 모듈 (물리, AI, 게임 로직, 게임 뷰 등)과 통신해야합니다. 이는 효율적인 이벤트 관리자를 설계해야 함을 의미합니다. 잘못 설계된 시스템은 특히 모바일 플랫폼에 영향을주는 CPU주기를 사용합니다.

이것이 입증되고 권장되는 접근법입니까? 사용 여부를 어떻게 결정해야합니까?


제임스 안녕하세요, 답변 주셔서 감사합니다. 나는이 책이 Event Driven 통신을 상당히 복잡한 시스템으로 묘사하는데 많은 CPU 리소스를 많이 소비하는 것처럼 보입니다.
Bunkai.Satori

답변에 넣고 스택 오버플로에 대한 답변으로 내가 준 이벤트 메시징 시스템의 개요에 대한 링크를 추가했습니다. 즐겨! 일부 사람들이 복잡하다고 생각하는 것은 반드시 그럴 필요는 없다는 것을 기억하십시오.
James


libuv최근에 성공적인 이벤트 라이브러리로 등장했습니다. C. Node.js 는 가장 유명한 사용 사례입니다.
Anko

답변:


46

이것은 제안 된대로 내 의견 을 전체 답변으로 확장 한 것입니다 .

, 단순하고 단순합니다. 의사 소통이 필요하며 '아직 있습니까?' 유형의 폴링이 필요하며, 일반적으로 다른 작업을 수행해야하는지 확인하는 데 시간이 낭비됩니다. 대신 그들이하라는 말에 반응하게 할 수 있습니다. 또한 객체 / 시스템 / 모듈간에 잘 정의 된 통신 경로는 병렬 설정을 크게 향상시킵니다.

Stack Overflow에서 이벤트 메시징 시스템에 대한 높은 수준의 개요를 제공했습니다 . 학교에서 전문 게임 타이틀과 게임 이외의 제품으로 사용했으며 물론 매번 사용 사례에 맞게 조정했습니다.

편집 : 메시지를 가져와야하는 객체어떻게 알 수 있는지대한 의견 질문을 처리하려면 : 객체 자체 Request에 이벤트에 대해 알려야합니다. 귀하의 EventMessagingSystem(EMS)는 Register(int iEventId, IEventMessagingSystem * pOjbect, (EMSCallback)fpMethodToUseForACallback)일치뿐만 아니라 객체 포인터와 콜백 Unregister에서 고유 한 항목을 생성 iEventId해야합니다. 이런 방식으로, 객체가 메시지를 알고 싶어 할 때 Register()시스템에서 할 수 있습니다 . 더 이상 이벤트에 대해 알 필요가 없으면Unregister(). 분명히, 이러한 콜백 등록 객체 풀과 목록에서 객체를 추가 / 제거하는 효과적인 방법을 원할 것입니다. (저는 일반적으로 자체 정렬 배열을 사용했습니다. 사용하지 않는 객체의 풀 스택과 필요할 때 크기를 이동시키는 배열 간의 자체 할당을 추적하는 멋진 방법입니다).

편집 : 업데이트 루프 및 이벤트 메시징 시스템으로 완벽하게 작동하는 게임의 경우 오래된 학교 프로젝트 를 확인하고 싶을 수도 있습니다 . 위에 링크 된 스택 오버 플로우 포스트도 참조합니다.


안녕하세요 제임스, 내부 이벤트 메시징 시스템에 대해 더 많이 생각할 때 엔진에 더 많은 비용을 들이지 않아도된다고 생각합니다. 예를 들어, 화면에 50 개의 개체가 있지만 그 중 5 개만 동작을 변경해야합니다. 전통적인 시스템 하에서, 모든 50 개의 개체는 그들이해야 할 일이 있는지 확인하기 위해 가능한 모든 행동을 점검해야합니다. 그러나 이벤트 메시징을 사용하면 특정 작업 변경으로 5 개의 메시지가 5 개의 메시지로 전송됩니다. 그것은 시간 절약 접근법처럼 보입니다.
Bunkai.Satori

위의 답변에서 연결된 시스템이 작동하는 방식은 객체가 원하는 메시지를 듣기 위해 등록하는 것입니다. 플레이어가 직접 상호 작용할 수있는 것을 '활성화'하여 약 10 정도 정도 청취하는 것들의 수를 유지 이 모든 유형의 시스템에서 '우리는 아직 있습니까?'보다 '똑똑해야'합니다. 폴링 시스템은 통신에 관한 한 엔진의 오버 헤드를 줄여야합니다.
James

혼란스럽게하는 이벤트 중심 시스템과 관련된 한 가지가 있습니다. 모든 객체에 정기적으로 호출되는 미리 정의 된 메소드 세트 (OnUpdate (), OnMove (), OnAI (), OnCollisionCheck () ...)가있는 기존 시스템에서 모든 객체는 상태 확인 및 관리 책임. 이벤트 중심 시스템에서는 모든 오브젝트의 일부 마스터 시스템 체킹 조건이 있어야하며 일부 이벤트가 감지 된 메시지를 메시지로 보내야합니다. 나에게 이것은 가능한 객체 상태를 표준화하고 독특한 객체 동작을 만들 수있는 자유를 제한합니다. 그게 사실입니까?
Bunkai.Satori

관찰자 패턴은 폴링의 일반적인 대안입니다. en.wikipedia.org/wiki/Observer_pattern
Ricket

1
@ hamlin11이 정보가 여전히 사람들을 돕고있어서 다행입니다 :) 등록과 관련하여, 이것이 아주 조금 발생하는 경우 속도를 최적화하고, 등록 객체 풀을 가져와야합니다.
James

22

내 의견은 당신이 게임을 시작하고 당신이 편한 것을 구현해야한다는 것입니다. 그렇게하면 MVC를 사용하지만 어떤 곳에서는 사용하지 않을 것입니다. 어떤 곳에서는 이벤트가 있지만 다른 곳에서는 그렇지 않습니다. 구성 요소 일부 장소, 상속 다른 장소; 깨끗한 장소를 디자인하고 다른 곳을 crufty 디자인하십시오.

완료되면 실제로 게임을 할 수 있고 게임은 메시지 전달 시스템보다 훨씬 시원하기 때문입니다.


2
안녕 조, 답변 주셔서 감사합니다, 나는 그것을 좋아합니다. 당신은 교묘 한 접근을 할 필요가 없다고 생각합니다. 작동 할 것으로 생각되는 응용 프로그램을 디자인해야하며 나중에 작동하지 않는 경우 다시 실행하면됩니다.
Bunkai.Satori

이것이 시스템이 존재하는 이유입니다. 많은 사람들이 당신이 말한 것을 행하고 악몽을 목격했으며 더 좋은 방법이 있어야한다고 말했습니다. 그들의 실수를 반복하거나 그들로부터 배우고 더 발전시킬 수 있습니다.
user441521

16

더 좋은 질문은 어떤 대안이 있습니까? 물리학, AI 등을 위해 올바르게 분할 된 모듈이있는 복잡한 시스템에서 이러한 시스템을 어떻게 조정할 수 있습니까?

메시지 전달은이 문제에 대한 "최상의"솔루션 인 것 같습니다. 나는 지금 대안을 생각할 수 없다. 그러나 실제로 메시지 전달의 많은 예가 있습니다. 실제로 운영 체제는 여러 기능에 대해 메시지 전달을 사용합니다. 에서 위키 백과 :

메시지는 프로세스 간 통신 수단과 같은 의미로 일반적으로 사용됩니다. 다른 일반적인 기술은 스트림 또는 파이프이며, 여기서 데이터는 일련의 기본 데이터 항목 (가상 버전의 가상 회로)으로 전송됩니다.

프로세스 간 통신은 운영 체제 환경 내에서 프로세스 간 (즉, 프로그램 인스턴스 실행) 통신입니다.

그렇다면 운영 체제에 충분하다면 게임에 충분할 것입니다. 다른 이점도 있지만 메시지 전달에 관한 Wikipedia 기사에서 설명 하도록하겠습니다 .

또한 "프로그램 내 메시지 전달을위한 데이터 구조?" 을 읽어보십시오.


안녕하세요 Ricket, 답변 주셔서 감사합니다. 게임 오브젝트가 서로 통신 할 수있는 다른 방법을 물었습니다. 내 마음에 오는 것은 직접 메서드 호출입니다. 그것은 매우 유연하여 많은 유연성을 제공하지는 않지만 이벤트 메시지 생성, 전달, 읽기 등을 피합니다. 우리는 여전히 고급 게임이 아닌 모바일 플랫폼에 대해 이야기합니다. 운영 체제는 내부 메시징 시스템을 많이 사용합니다. 그러나 작은 지연으로도 중단이 발생하는 실시간으로 실행되도록 설계되지 않았습니다.
Bunkai.Satori

이 질문을 잠시 동안 열어 두겠습니다. 닫기 전에 더 많은 의견을 수집하고 싶습니다.
Bunkai.Satori

직접 메소드 호출은 일반적으로 서로를 호출하는 클래스를 결합시킵니다. 이것은 상호 교환이 가능한 구성 요소로 분리 된 시스템에는 좋지 않습니다. 응집력이 적은 직접 메소드 호출을 용이하게하는 몇 가지 패턴이 있습니다 (예 : MVC의 "컨트롤러"부분은 기본적으로 뷰의 모델 쿼리를 용이하게하기 위해이 목적을 제공합니다). 일반적으로 메시지 전달은 유일한 방법입니다. 서브 시스템간에 제로 커플 링 (또는 적어도 내가 아는 유일한 방법)을 갖는 시스템.
Ricket

아, 이제 패턴을 논의하기 시작했습니다. 이 프로그래밍 방식에 대해 조금 들었습니다. 이제 패턴이 무엇인지 이해하기 시작합니다. 응용 프로그램 디자인과는 완전히 다릅니다. 동일한 코드를 사용하여 모든 객체를 확인하면서 객체를 꼼꼼하게 제어합니다. 객체를 확인한 후 그에 따라 속성을 설정합니다.
Bunkai.Satori

1
"데이터 구조 ..."질문에 놀라운 답변이 있습니다. 내가 선택한 대답과 함께 제공되는 Nebula3 기사는 내가 본 크기의 게임 엔진 아키텍처에 대한 최고의 설명입니다.
deft_code

15

메시지는 일반적으로 다음과 같은 경우에 잘 작동합니다.

  1. 메시지를 보내는 것은 메시지가 수신되는지 상관하지 않습니다.
  2. 발신자는 수신자로부터 즉각적인 응답을받을 필요가 없습니다.
  3. 단일 발신자를 수신하는 여러 수신자가있을 수 있습니다.
  4. 메시지가 자주 또는 예기치 않게 전송됩니다. 즉, 모든 객체가 단일 프레임마다 "업데이트"메시지를 받아야하는 경우 메시지는 실제로 많은 돈을 구매하지는 않습니다.

귀하의 요구가 해당 목록과 일치할수록 적합한 메시지가 더 좋습니다. 매우 일반적인 수준에서, 그들은 꽤 좋습니다. 폴링이나 다른 글로벌 솔루션과 달리 아무것도하지 않기 위해 CPU 사이클을 낭비하지 않는 것이 좋습니다. 코드베이스의 일부를 분리하는 데 환상적이며 항상 도움이됩니다.


4

제임스와 리켓에서 지금까지 큰 답변을 드리겠습니다. 메시지 전달 / 이벤트 중심 커뮤니케이션은 개발자의 무기고에서 중요한 도구이지만, 쉽게 과용 될 수 있습니다. 망치가 있으면 모든 것이 못처럼 보입니다.

당신은 절대적으로 100 %가 타이틀 주변의 데이터 흐름에 대해 생각하고 정보가 한 서브 시스템에서 다른 서브 시스템으로 전달되는 방식을 완전히 알고 있어야합니다. 어떤 경우에는 메시지 전달이 가장 좋습니다. 다른 시스템에서는 하나의 서브 시스템이 공유 오브젝트 목록을 조작하는 것이 더 적절할 수 있으며, 다른 서브 시스템도 동일한 공유 목록을 조작 할 수 있습니다. 그리고 더 많은 방법이 있습니다.

항상 어떤 서브 시스템이 어떤 데이터에 액세스해야하고 언제 데이터에 액세스해야하는지 알고 있어야합니다. 이는 병렬화 및 최적화 능력에 영향을 미치며 엔진의 다른 부분이 너무 밀접하게 얽혀있을 때보다 교활한 문제를 피하는 데 도움이됩니다. 불필요한 데이터 복사를 최소화하고 데이터 레이아웃이 캐시 사용에 미치는 영향을 고려해야합니다. 이 모든 것이 각 경우에 가장 적합한 솔루션으로 안내합니다.

즉, 내가 작업 한 거의 모든 게임에는 견고한 비동기 메시지 전달 / 이벤트 알림 시스템이 있습니다. 복잡하고 빠르게 변화하는 설계 요구에도 불구하고 간결하고 효율적인 유지 보수가 가능한 코드를 작성할 수 있습니다.


좋은 추가 정보를 얻으려면 +1 안녕하세요, MrCranky, 감사합니다. 당신이 말한 것에 따르면, 모바일 게임에서도 이벤트 메시징 시스템을 고려할 가치가 있습니다. 그러나 계획을 간과해서는 안되며 메시징 시스템을 사용할 위치를 잘 고려해야합니다.
Bunkai.Satori

4

글쎄, 나는이 게시물이 꽤 오래되었다는 것을 알고 있지만 저항 할 수는 없었다.

최근에 게임 엔진을 만들었습니다. 3d 파티 라이브러리를 사용하여 렌더링 및 물리학을 사용하지만 엔티티와 게임 논리를 정의하고 처리하는 핵심 부분을 작성했습니다.

엔진은 반드시 전통적인 접근 방식을 따릅니다. 모든 엔티티에 대한 업데이트 기능을 호출하는 기본 업데이트 루프가 있습니다. 충돌은 엔티티에 대한 콜백으로 직접보고됩니다. 엔터티 간 통신은 엔터티간에 교환되는 스마트 포인터를 사용하여 이루어집니다.

엔진 메시지에 대한 작은 엔티티 그룹 만 처리하는 기본 메시지 시스템이 있습니다. 이러한 메시지는 업데이트 목록을 망칠 수 있으므로 게임 상호 작용이 끝날 때 처리하는 것이 좋습니다 (예 : 엔티티 작성 또는 파기). 따라서 각 게임 루프가 끝나면 작은 메시지 목록이 사용됩니다.

원시 메시지 시스템에도 불구하고, 나는 시스템이 대부분 "업데이트 루프 기반"이라고 말하고 싶습니다.

잘. 이 시스템을 사용한 후에는 매우 간단하고 빠르며 체계적으로 구성되어 있다고 생각합니다. 게임 로직은 메시지 큐와 같이 동적이 아닌 엔티티 내부에서 볼 수 있고 자체적으로 포함됩니다. 필자의 의견으로는 이벤트 시스템이 게임 로직에 불필요한 복잡성을 유발하고 게임 코드를 이해하고 디버깅하기가 매우 어렵 기 때문에 실제로 이벤트 중심으로 만들지 않을 것입니다.

그러나 나는 또한 내 것과 같은 순수한 "업데이트 루프 기반"시스템에도 문제가 있다고 생각한다.

예를 들어, 어떤 순간에, 하나의 엔티티는 "무 상태"에있을 수 있고, 플레이어가 다가오는 것을 기다리거나 다른 것을 기다리고있을 수 있습니다. 이러한 경우 대부분의 경우 엔터티는 프로세서 시간을 전혀 소모하지 않으며 엔터티를 끄고 특정 이벤트가 발생하면 켜는 것이 좋습니다.

다음 게임 엔진에서는 다른 접근 방식을 채택하겠습니다. 엔터티는 업데이트, 그리기, 충돌 감지 등과 같은 엔진 작업에 등록됩니다. 이러한 각 이벤트에는 실제 엔터티에 대한 별도의 엔터티 인터페이스 목록이 있습니다.


엔터티는 게임의 복잡성으로 관리하기 어려운 괴물 코드 기반이됩니다. 그것들을 함께 연결하면 기능을 추가하거나 변경하기가 빨라집니다. 기능을 작은 구성 요소로 나누면 기능을보다 쉽게 ​​유지 관리하고 추가 할 수 있습니다. 그렇다면 이러한 구성 요소는 어떻게 통신합니까? 답은 한 구성 요소에서 다른 구성 요소의 함수를 발생시키면서 기본 데이터를 인수와 함께 전달하는 이벤트입니다.
user441521

3

예. 게임 시스템이 서로 통신 할 수있는 매우 효율적인 방법입니다. 이벤트는 많은 시스템을 분리하고 서로의 존재를 몰라도 개별적으로 컴파일 할 수있게합니다. 즉, 클래스를보다 쉽게 ​​프로토 타입 할 수 있고 컴파일 시간이 더 빠릅니다. 더 중요한 것은 종속성 혼란 대신 플랫 코드 디자인으로 끝나는 것입니다.

이벤트의 또 다른 큰 장점은 네트워크 나 다른 텍스트 채널을 통해 쉽게 스트리밍 할 수 있다는 것입니다. 나중에 재생할 수 있도록 녹화 할 수 있습니다. 가능성은 끝이 없습니다.

다른 이점 : 동일한 이벤트를 수신하는 여러 서브 시스템을 가질 수 있습니다. 예를 들어, 모든 원격 뷰 (플레이어)는 엔터티 생성 이벤트에 자동으로 가입하고 각 클라이언트에서 엔터티를 생성 할 수 있습니다. 이벤트를 사용하지 않으면 얼마나 많은 작업을 수행 할 수 있는지 상상해보십시오. Update()어딘가에 전화를 걸거나 view->CreateEntity게임 로직 (뷰에 대한 지식과 필요한 정보가없는 곳)에서 전화 해야 할 수도 있습니다. 이벤트 없이는이 문제를 해결하기가 어렵습니다.

이벤트를 통해 이벤트를 구독하고 게임에서 어떤 일이 발생할 때 모든 일을 할 수있는 무한한 종류의 무한한 수의 객체를 지원하는 우아하고 분리 된 솔루션을 얻을 수 있습니다. 그래서 이벤트가 큰 이유입니다.

자세한 내용은 여기를 참조 하십시오 .


일방적이지만 좋은 점. 나는 항상 일반성을 의심한다 : 사건에 대한 반 이용 사례가 있는가?
Anko

1
사용 금지 지점 : 직접 대응해야하는 경우. 어떤 이벤트에 대해 원하는 것은 항상 직접 및 동일한 스레드에서 실행됩니다. 외부 지연이 전혀없는 경우 통화 완료가 지연 될 수 있습니다. 선형 종속성 만있는 경우 거의 동시에 여러 작업을 수행하지 않는 경우. node.js를 보면 모든 io 호출은 이벤트 기반입니다. Node.js는 이벤트가 100 % 올바르게 구현 된 곳입니다.
user2826084

이벤트 시스템은 비동기 일 필요는 없습니다. 코 루틴을 사용하여 비동기를 시뮬레이션하는 동시에 필요할 때 동기화 할 수 있습니다.
user441521

2

내가 본 것에서 메시징을 완전히 기반으로 한 엔진을 갖는 것은 그리 일반적이지 않습니다. 물론 네트워킹, GUI 및 기타와 같은 메시징 시스템에 매우 적합한 하위 시스템이 있습니다. 일반적으로 생각할 수있는 몇 가지 문제가 있습니다.

  • 게임 엔진은 많은 양의 데이터를 처리합니다.
  • 게임은 빨라야합니다 (20-30 FPS가 최소 여야 함).
  • 어떤 일이 언제 행해졌는지 또는 언제 행해질 것인지를 아는 것이 종종 필요합니다.

"가능한 한 효율적이어야한다"라는 일반적인 게임 개발자의 접근으로, 이러한 메시징 시스템은 그리 일반적이지 않다.

그러나 나는 당신이 그냥 가서 시도해야한다는 것에 동의합니다. 이러한 시스템에는 많은 이점이 있으며 오늘날 컴퓨팅 성능을 저렴하게 사용할 수 있습니다.


나는 이것에 동의하는지 모른다. 대안은 낭비적인 폴링입니다. 이벤트에만 응답하는 것이 가장 효율적입니다. 예, 이벤트가 발생하고 이벤트를 발생시키는 폴링이 여전히 있지만 이벤트 지향적 인 항목도 많이 있습니다.
user441521

나는 이것에도 동의하지 않습니다. 이벤트 메시징이 서브 시스템-서브 시스템 통신을 위해 설계 되었기 때문에 처리하는 데이터 게임 엔진의 양은 크게 관련이 없습니다. 게임 오브젝트는 다른 게임 오브젝트와 직접 통신해서는 안됩니다 (오브젝트의 커스텀 이벤트 핸들러는 예외 일 수 있습니다). 비교적 적은 수의 서브 시스템과 "관심있는"영역으로 인해 다중 스레드 엔진에서 잘 설계된 메시징 백본의 성능 비용은 무시할 수 있습니다.
이안 영
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.