컴포넌트 기반 설계 : 객체 상호 작용 처리


9

구성 요소 기반 디자인에서 객체가 다른 객체와 정확히 어떻게 일치하는지 잘 모르겠습니다.

Obj수업 이 있다고 가정 해 봅시다 . 나는한다:

Obj obj;
obj.add(new Position());
obj.add(new Physics());

그러면 공을 움직일뿐만 아니라 물리를 적용 할 수있는 다른 물체를 어떻게 가질 수 있을까요? 구현 세부 정보를 찾지 않고 객체가 어떻게 통신하는지 추상적으로 찾고 있습니다. 엔터티 기반 디자인에서 다음을 가질 수 있습니다.

obj1.emitForceOn(obj2,5.0,0.0,0.0);

구성 요소 중심 설계를 이해하고 기본 작업을 수행하는 방법에 대한 기사 나 설명은 실제로 도움이 될 것입니다.

답변:


10

일반적으로 메시지를 사용하여 수행됩니다. 당신 같은이 사이트에 다른 질문에 세부 정보를 많이 찾을 수 있습니다 여기에 또는 이를 .

구체적인 예를 들어,가는 방법은 Message객체가 처리 할 수 있는 작은 클래스 를 정의하는 것입니다 .

struct Message
{
    Message(const Objt& sender, const std::string& msg)
        : m_sender(&sender)
        , m_msg(msg) {}
    const Obj* m_sender;
    std::string m_msg;
};

void Obj::Process(const Message& msg)
{
    for (int i=0; i<m_components.size(); ++i)
    {
        // let components do some stuff with msg
        m_components[i].Process(msg);
    }
}

이런 방식으로 Obj클래스 관련 인터페이스를 "오염"시키지 않습니다 . 일부 구성 요소는 메시지를 처리하도록 선택할 수 있으며 일부 구성 요소는 메시지를 무시할 수 있습니다.

다른 객체에서이 메소드를 직접 호출하여 시작할 수 있습니다.

Message msg(obj1, "EmitForce(5.0,0.0,0.0)");
obj2.ProcessMessage(msg);

이 경우, obj2의는 Physics메시지를 선택하고, 처리는 할 필요가 무엇이든지 할 것입니다. 완료되면 다음 중 하나입니다.

  • Position컴포넌트가 선택 하도록 "SetPosition"메시지를 자신에게 보냅니다 .
  • 또는 Position수정을 위해 구성 요소에 직접 액세스하십시오 (모든 객체에 Position구성 요소 가 있다고 가정 할 수는 없지만 Position구성 요소는의 요구 사항 일 수 있음 Physics).

일반적으로 메시지의 실제 처리를 다음 구성 요소의 업데이트 로 지연 시키는 것이 좋습니다 . 즉시 처리한다는 것은 다른 객체의 다른 구성 요소로 메시지를 보내는 것을 의미하므로 한 메시지 만 보내면 불가분의 스파게티 스택을 의미 할 수 있습니다.

비동기 메시지 대기열, 객체 그룹에 메시지 전송, 구성 요소 별 메시지 등록 / 등록 취소 등 나중에 고급 시스템을 사용해야 할 것입니다.

Message위,하지만 런타임에 문자열을 처리하는 것은 정말 비효율적이기 때문에 클래스는 단순한 문자열을 일반 용기가 될 수 있습니다. 문자열, 정수, 부동 소수점 등 일반 값의 컨테이너를 사용할 수 있습니다. 이름이나 더 나은 ID를 사용하여 다른 유형의 메시지를 구별 할 수 있습니다. 또는 특정 요구에 맞는 기본 클래스를 파생시킬 수도 있습니다. 귀하의 경우 원하는 힘 벡터 EmitForceMessage에서 파생 Message되고 추가 되는 RT를 상상할 수 있지만 RTTI 의 런타임 비용에주의하십시오 .


3
컴포넌트에 직접 액세스하는 "비 순도"에 대해서는 걱정하지 않습니다. 구성 요소는 학계가 아니라 기능 및 디자인 요구를 충족시키는 데 사용됩니다. 구성 요소가 존재하는지 확인하려고합니다 (예 : 구성 요소 가져 오기 호출에 대한 점검 리턴 값이 널이 아님).
Sean Middleditch

RTTI를 사용하여 마지막으로 말한 것처럼 항상 생각했지만 RTTI에 대해 많은 사람들이 말한 많은 것들
jmasterx

@SeanMiddleditch 물론, 같은 방식으로 다른 엔티티에 액세스 할 때 수행중인 작업을 항상 다시 확인해야한다는 것을 분명히하기 위해이 방법을 사용합니다.
Laurent Couvidou

@Milo 컴파일러로 구현 된 RTTI와 그 병목 현상이 발생할 dynamic_cast 있지만 지금은 걱정하지 않아도됩니다. 나중에 문제가되는 경우에도이를 최적화 할 수 있습니다. CRC 기반 클래스 식별자는 매력처럼 작동합니다.
Laurent Couvidou

템플릿 <typename T> uint32_t class_id () {정적 uint32_t v; 리턴 (uint32_t) & v; } ´-RTTI가 필요하지 않습니다.
arul

3

내가 보여주는 것과 비슷한 문제를 해결하기 위해 내가 한 것은 특정 구성 요소 처리기를 추가하고 일종의 이벤트 해결 시스템을 추가하는 것입니다.

따라서 "Physics"개체의 경우 초기화 될 때 Physics objetcs의 중앙 관리자에 추가됩니다. 게임 루프에서 이러한 종류의 관리자에는 자체 업데이트 단계가 있으므로이 PhysicsManager가 업데이트되면 모든 물리 상호 작용을 계산하고이를 이벤트 대기열에 추가합니다.

모든 이벤트를 생성 한 후 이벤트 큐를 해결하여 발생한 상황을 확인하고 이에 따라 조치를 취할 수 있습니다. 경우에 따라 객체 A와 B가 상호 작용했다는 이벤트가 있어야하므로 emitForceOn 메소드를 호출하십시오.

이 방법의 장점 :

  • 개념적으로 따라하기가 정말 간단합니다.
  • quadtress 사용 또는 필요한 것을 사용하는 것과 같은 특정 최적화를위한 공간을 제공합니다.
  • 결국 "플러그 앤 플레이"가됩니다. 물리가있는 객체는 관리자가 존재하지 않기 때문에 비 물리 객체와 상호 작용하지 않습니다.

단점 :

  • 구성 요소에서 구성 요소 소유자, 관리자에서 구성 요소, 이벤트에서 참가자에 이르기까지 조심하지 않으면 모든 것을 올바르게 청소하는 것이 약간 지저분해질 수 있습니다. ).
  • 모든 것을 해결하는 순서에 특별한 생각을해야합니다. 나는 그것이 당신의 경우가 아니라고 생각하지만, 이벤트가 다른 이벤트를 생성하고 이벤트 큐에 직접 추가하는 하나 이상의 무한 루프에 직면했습니다.

이게 도움이 되길 바란다.

추신 : 누군가가 이것을 해결하는 더 깨끗하고 나은 방법을 가지고 있다면, 정말로 듣고 싶습니다.


1
obj->Message( "Physics.EmitForce 0.0 1.1 2.2" );
// and some variations such as...
obj->Message( "Physics.EmitForce", "0.0 1.1 2.2" );
obj->Message( "Physics", "EmitForce", "0.0 1.1 2.2" );

이 디자인에서주의해야 할 사항 :

  • 구성 요소 이름이 첫 번째 매개 변수입니다. 이는 메시지에서 너무 많은 코드 작업을하지 않기위한 것입니다. 메시지가 트리거 할 구성 요소를 알 수 없습니다. 모든 구성 요소가 90 % 실패로 메시지를 씹는 것을 원하지 않습니다. 많은 불필요한 가지와 strcmp 로 변환하는 비율 .
  • 메시지 이름은 두 번째 매개 변수입니다.
  • 첫 번째 점 (# 1 및 # 2)은 필요하지 않으며 단지 컴퓨터가 아닌 사람들을 위해 더 쉽게 읽을 수 있도록하기위한 것입니다.
  • sscanf, iostream, you-name-it 호환입니다. 메시지 처리를 단순화 할 수있는 구문 설탕이 없습니다.
  • 하나의 문자열 매개 변수 : 원시 유형을 전달하는 것은 상대적으로 알려지지 않은 유형의 알 수없는 매개 변수를 지원해야하기 때문에 메모리 요구 사항 측면에서 더 저렴하지 않습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.