구성 요소 엔터티 시스템 아키텍처를 사용하여 게임이 아닌 응용 프로그램을 구축하는 것이 합리적입니까?


24

Apple AppStore 또는 Google Play 앱 스토어와 같은 응용 프로그램 (기본 또는 웹)을 작성할 때 Model-View-Controller 아키텍처를 사용하는 것이 일반적이라는 것을 알고 있습니다.

그러나 게임 엔진에서 공통적 인 Component-Entity-System 아키텍처를 사용하여 응용 프로그램을 만드는 것도 합리적입니까?


1
라이트 테이블의 구조를 체크 아웃 : chris-granger.com/2013/01/24/the-ide-as-data
하칸 Deryal에게

답변:


39

그러나 게임 엔진에서 공통적 인 Component-Entity-System 아키텍처를 사용하여 응용 프로그램을 만드는 것도 합리적입니까?

나에게, 절대적으로. 저는 Visual FX에서 일하며이 분야의 다양한 시스템, (CAD / CAM을 포함한) 아키텍처, SDK에 대한 배가 고픈 것 같습니다. 가장 미묘한 것조차도 항상 미묘한 영향을 미치지는 않습니다.

VFX는 렌더링 된 결과를 표시하는 뷰포트가있는 "장면"이라는 하나의 중심 개념이 있다는 점에서 게임과 유사합니다. 또한 물리 상황이 발생할 수있는 파티클 이미 터, 파티클이 스폰되는 파티클 이미 터, 애니메이션 및 렌더링 된 메시, 모션 애니메이션 등이있을 수 있습니다. 마지막에 사용자에게 모두.

최소한 매우 복잡한 게임 엔진과 비슷한 또 다른 개념은 디자이너가 자체적으로 스크립트를 작성하는 기능 (스크립트 및 노드)을 포함하여 장면을 유연하게 디자인 할 수있는 "디자이너"측면의 필요성이었습니다.

몇 년 동안 ECS가 가장 적합하다는 것을 알았습니다. 물론 그것은 주관성과 완전히 이혼하지는 않았지만 가장 적은 문제를 일으키는 것으로 강하게 나타납니다. 그것은 우리가 항상 어려움을 겪고있는 훨씬 더 큰 문제를 해결하는 한편, 우리에게 몇 가지 새로운 사소한 문제만을 대가로주었습니다.

전통적인 OOP

설계 요구 사항을 미리 파악하지만 구현 요구 사항을 제대로 파악하지 못하면보다 전통적인 OOP 접근 방식이 실제로 강력 할 수 있습니다. 더 평평한 다중 인터페이스 접근 방식 또는보다 중첩 된 계층 적 ABC 접근 방식을 통해 구현을보다 쉽고 안전하게 변경하면서 디자인을 구체화하고 변경하기가 더 어려워지는 경향이 있습니다. 단일 버전을 지나는 모든 제품에는 항상 불안정성이 필요하므로 OOP 접근 방식은 설계 수준에 대한 안정성 (변경의 어려움 및 변경 이유가 없음)과 불안정성 (변경의 용이성 및 변경 이유)을 왜곡하는 경향이 있습니다. 구현 수준으로.

그러나 진화하는 사용자 엔드 요구 사항에 대비하여 디자인과 구현 모두 자주 변경해야 할 수 있습니다. 식물과 동물이 동시에 있어야하는 유사 생물체에 대한 강력한 사용자 엔드 요구와 같은 이상한 것을 발견 할 수 있으며, 구축 한 전체 개념 모델이 완전히 무효화됩니다. 일반적인 객체 지향 접근 방식은 여기서 당신을 보호하지 않으며 때로는 예상치 못한 개념을 바꾸는 변경을 더욱 어렵게 만들 수 있습니다. 성능이 매우 중요한 영역이 관련된 경우 설계 변경 이유가 더 늘어납니다.

객체의 적합한 인터페이스를 형성하기 위해 여러 개의 세분화 된 인터페이스를 결합하면 클라이언트 코드를 안정화하는 데 많은 도움이 될 수 있지만 때때로 클라이언트 종속성의 수를 손상시킬 수있는 하위 유형을 안정화하는 데 도움이되지는 않습니다. 예를 들어 시스템의 일부에서만 하나의 인터페이스를 사용할 수 있지만 해당 인터페이스를 구현하는 수천 개의 하위 유형이 있습니다. 이 경우 복잡한 하위 유형을 유지 (복잡한 인터페이스 책임을 수행해야하기 때문에 복잡한 하위 유형)을 유지하면 인터페이스를 통해 코드를 사용하는 것보다 악몽이 될 수 있습니다. OOP는 복잡성을 개체 수준으로 전송하는 경향이 있지만 ECS는이를 클라이언트 ( "시스템") 수준으로 전송하며, 시스템이 거의 없지만 일치하는 "개체"( "엔터티")가 많을 때 이상적입니다.

여기에 이미지 설명을 입력하십시오

또한 클래스는 데이터를 비공개로 소유하므로 불변량을 자체적으로 유지할 수 있습니다. 그럼에도 불구하고 객체가 서로 상호 작용할 때 실제로 유지하기 어려운 "거친"불변이 있습니다. 복잡한 시스템이 전체적으로 유효한 상태가 되려면 개별 불변 값이 올바르게 유지 되더라도 복잡한 객체 그래프를 고려해야합니다. 기존의 OOP 스타일 접근 방식은 세분화 된 불변량을 유지하는 데 도움이 될 수 있지만 객체가 시스템의 작은 측면에 초점을 둔 경우 광범위하고 거친 불변량을 유지하기가 실제로 어려워 질 수 있습니다.

이러한 종류의 레고 블록 구축 ECS 접근 방식이나 변형이 도움이 될 수있는 곳입니다. 또한 시스템이 일반적인 객체보다 더 거칠게 설계되면 시스템의 조감도에서 이러한 종류의 불변량을 유지하는 것이 더 쉬워집니다. 아주 작은 물체의 상호 작용은 1km의 종이를 포함하는 의존성 그래프로 작은 작은 물체에 초점을 맞추는 작은 작은 물체 대신 하나의 광범위한 작업에 초점을 맞춘 하나의 큰 시스템으로 바뀝니다.

그러나 나는 항상 데이터 지향적 사고 방식 중 하나 였지만 ECS에 대해 배우기 위해 게임 업계의 현장 밖을 봐야했습니다. 또한, 재미있게도, 나는 반복해서 더 나은 디자인을 만들어 내기 위해 ECS를 향한 길을 거의 다 갔다. 나는 그것을 완전히 만들지 않았고 "시스템"부분의 공식화와 원시 데이터에 이르기까지 구성 요소를 축소하는 매우 중요한 세부 사항을 놓쳤다.

ECS에 정착 한 방법과 이전 설계 반복의 모든 문제를 해결하는 방법을 살펴 보겠습니다. ECS가 게임 산업을 넘어서서 적용될 수 있다는 점에서 여기에 대한 답변이 매우 강력한“예”가 될 수있는 이유를 정확하게 강조하는 데 도움이 될 것입니다.

1980 년대 무차별 구조

제가 VFX 업계에서 처음으로 작업 한 아키텍처는 회사에 합류 한 지 이미 10 년이 지난 오랜 유산이었습니다. 그것은 C를 완전히 무차별 적으로 코딩하는 C였습니다. 미니어처 및 지나치게 단순한 슬라이스는 다음과 같은 종속성과 유사합니다.

여기에 이미지 설명을 입력하십시오

그리고 이것은 하나의 작은 시스템 조각에 대한 엄청나게 단순화 된 다이어그램입니다. 다이어그램의 각 클라이언트 ( "렌더링", "물리", "모션")는 다음과 같이 유형 필드를 확인하는 "일반"개체를 얻습니다.

void transform(struct Object* obj, const float mat[16])
{
    switch (obj->type)
    {
        case camera:
            // cast to camera and do something with camera fields
            break;
        case light:
            // cast to light and do something with light fields
            break;
        ...
    }
}

물론 이것보다 훨씬 더 추하고 복잡한 코드가 있습니다. 스위치를 반복해서 반복해서 반복적으로 수행하는 이러한 스위치 케이스에서 추가 기능이 호출되는 경우가 종종 있습니다. 이 다이어그램과 코드는 거의 ECS 라이트처럼 보일 수도 있지만, 더 강한 엔터티 구성 요소를 구분 ( "없었다 이 객체가 카메라?", "이 객체하지 않습니다 제공 (모션을?") 및 "시스템"의 어떤 형식화는 여러 곳에서 중첩 된 함수 만 사용하고 책임을 혼합합니다. 이 경우 거의 모든 것이 복잡해졌으며 어떤 기능이든 재난이 발생할 가능성이있었습니다.

여기서 테스트 절차는 종종 동일한 유형의 항목이 동일한 경우에도 다른 유형의 항목과 분리 된 메시와 같은 항목을 확인해야했습니다. 여기서 코딩의 무차별적인 성격 (종종 많은 복사 및 붙여 넣기가 수반 됨) 그렇지 않으면 정확히 같은 논리가 한 항목 유형에서 다른 항목 유형으로 실패 할 수 있습니다. 새로운 유형의 항목을 처리하기 위해 시스템을 확장하려고 시도하는 것은 희망이 없었습니다. 우리는 기존 유형의 항목을 처리하기 위해 너무 많은 어려움을 겪었을 때 너무 어려웠 기 때문에 강력하게 표현 된 사용자 엔드 요구 사항이있었습니다.

일부 전문가 :

  • 어 ... 엔지니어링 경험이 없어요. 이 시스템은 다형성과 같은 기본 개념에 대한 지식이 필요하지 않으므로 완전히 무차별 적입니다. 그래서 초보자조차도 디버깅 전문가가 간신히 유지할 수는 있지만 일부 코드를 이해할 수 있다고 생각합니다.

몇 가지 단점 :

  • 유지 보수의 악몽. 마케팅 팀은 실제로 3 년 주기로 2000 개가 넘는 고유 한 버그를 수정해야한다고 생각했습니다. 나에게 그것은 처음에 많은 버그가 있었다는 것에 당황스러워하며, 그 과정은 아마도 항상 증가하고있는 버그의 약 10 % 만 수정했을 것입니다.
  • 가장 융통성이없는 솔루션에 대해.

1990 년대 COM 아키텍처

VFX 업계의 대부분은 내가 수집 한 것에서이 스타일의 아키텍처를 사용하여 설계 결정에 대한 문서를 읽고 소프트웨어 개발 키트를 살펴 봅니다.

ABI 수준에서 정확히 COM이 아닐 수도 있습니다 (이러한 아키텍처 중 일부는 동일한 컴파일러를 사용하여 작성된 플러그인 만 가질 수 있음). 구성 요소가 지원하는 인터페이스를 확인하기 위해 객체에 대한 인터페이스 쿼리와 많은 유사한 특성을 공유합니다.

여기에 이미지 설명을 입력하십시오

이런 종류의 접근 방식으로 transform위 의 유추 함수는 다음과 같은 형태로 나타납니다.

void transform(Object obj, const Matrix& mat)
{
    // Wrapper that performs an interface query to see if the 
    // object implements the IMotion interface.
    MotionRef motion(obj);

    // If the object supported the IMotion interface:
    if (motion.valid())
    {
        // Transform the item through the IMotion interface.
        motion->transform(mat);
        ...
    }
}

이것이 이전 코드베이스의 새로운 팀이 정착 한 접근법으로 결국 리팩토링합니다. 유연성과 유지 관리 성 측면에서 원본보다 크게 개선되었지만 다음 섹션에서 다루게 될 몇 가지 문제가 여전히있었습니다.

일부 전문가 :

  • 이전의 무차별 대입 솔루션보다 훨씬 유연하고 확장 가능합니다.
  • 모든 인터페이스를 완전히 추상적 (상태 비 저장, 구현 없음, 순수한 인터페이스 만)으로 만들어 SOLID의 여러 원칙을 강력하게 준수합니다.

몇 가지 단점 :

  • 많은 상용구. 우리의 컴포넌트는 객체를 인스턴스화하기 위해 레지스트리를 통해 게시되어야했으며, 지원하는 인터페이스는 인터페이스를 상속 (Java에서 "구현")하고 쿼리에서 사용할 수있는 인터페이스를 나타내는 코드를 제공해야했습니다.
  • 순수한 인터페이스의 결과로 모든 곳에서 복제 된 로직을 홍보했습니다. 예를 들어, 구현 된 모든 구성 요소는 모든 기능에 대해 IMotion항상 동일한 상태와 동일한 구현을 갖습니다. 이를 완화하기 위해 시스템 전체에서 기본 인터페이스와 도우미 기능을 중앙 집중화하여 동일한 인터페이스에 대해 동일한 방식으로 동일한 방식으로 구현되는 경향이 있으며 여러 가지 상속이 가능할 것입니다. 클라이언트 코드가 쉽지만 혼란 스러울 수 있습니다.
  • 비 효율성 : vtune 세션에서는 기본 QueryInterface기능이 거의 항상 중간에서 상위 핫스팟으로 표시되고 때로는 1 위 핫스팟으로 표시되는 경우가 종종있었습니다. 이를 완화하기 위해 코드베이스 캐시의 일부를 이미 지원하는 것으로 알려진 객체 목록을 렌더링하는 것과 같은 작업을 수행합니다.IRenderable하지만 복잡성과 유지 보수 비용이 크게 증가했습니다. 마찬가지로, 이것은 측정하기가 더 어려웠지만 모든 단일 인터페이스에 동적 디스패치가 필요할 때 이전에 수행했던 C 스타일 코딩에 비해 약간의 속도 저하가 있음을 발견했습니다. 지점의 잘못된 예측 및 최적화 장벽과 같은 것들은 약간의 코드 외부에서 측정하기가 어렵지만 사용자는 일반적으로 사용자 인터페이스의 응답 성과 이전 버전과 최신 버전의 소프트웨어를 나란히 비교하여 악화되는 것을 알았습니다. 알고리즘 복잡성이 변하지 않은 영역의 경우에는 상수 만 있습니다.
  • 더 넓은 시스템 수준에서 정확성에 대해 추론하기는 여전히 어려웠습니다. 이전의 접근 방식보다 훨씬 쉬웠지만이 시스템 전체에서 객체 간 복잡한 상호 작용을 파악하기가 특히 어려웠습니다. 특히 최적화가 필요해지기 시작했습니다.
  • 인터페이스를 올바르게 설정하는 데 문제가있었습니다. 인터페이스를 사용하는 시스템에는 하나의 광범위한 장소 만있을 수 있지만 사용자 엔드 요구 사항은 버전에 따라 변경 될 수 있으며, 새로운 기능을 추가하기 위해 인터페이스를 구현하는 모든 클래스에 계단식 변경을 수행해야합니다. 인터페이스, 예를 들어, 이미 후드 아래에서 로직을 중앙 집중화하고있는 추상 기본 클래스가 없다면 (이 중 일부는 이러한 반복적 인 변경을 반복적으로 반복하지 않기를 희망하면서 이러한 계단식 변경의 중간에 나타납니다).

여기에 이미지 설명을 입력하십시오

실용적인 응답 : 구성

우리가 문제를 일으킨 전에 (또는 적어도 나는) 알고있는 것 중 하나는 IMotion100 개의 다른 클래스로 구현되었지만 정확히 동일한 구현 및 상태와 관련이 있다는 것입니다. 또한 렌더링, 키 프레임 모션 및 물리와 같은 소수의 시스템에서만 사용됩니다.

따라서 이러한 경우 인터페이스에 대한 인터페이스를 사용하는 시스템과 인터페이스에 대한 인터페이스를 구현하는 하위 유형 사이에 100 대 1의 관계가있을 수 있습니다.

복잡성 및 유지 관리는에 의존하는 3 개의 클라이언트 시스템 대신 100 개의 하위 유형의 구현 및 유지 관리로 크게 왜곡됩니다 IMotion. 이로 인해 인터페이스를 사용하는 3 곳이 아닌 100 가지 하위 유형을 유지 관리하는 데 모든 유지 관리 문제가 해결되었습니다. "간접적 인 독립 커플 링"이 거의 없거나 전혀없는 코드에서 3 개의 장소 업데이트 (직접 종속성이 아닌 인터페이스를 통해 간접적으로는 간접적으로 연결됨) , 꽤 큰 거래 *.

* 구현 관점에서 이런 의미에서 "상호 커플 링"의 정의를 망쳐 놓는 것이 이상하고 잘못이라는 것을 깨달았습니다. 인터페이스와 백개 하위 유형의 해당 구현 모두와 관련된 유지 관리 복잡성을 설명하는 더 좋은 방법을 찾지 못했습니다. 변경해야합니다.

그래서 나는 열심히 밀어 붙여야했지만 좀 더 실용적이되고 전체적인 "순수 인터페이스"아이디어를 완화하려고 노력했다. IMotion다양한 구현을 통해 이점을 얻지 않는 한 완전히 추상적이고 상태가없는 것과 같은 것을 만드는 것은 의미가 없습니다 . 우리의 경우, IMotion다양한 구현을 위해서는 다양성을 원하지 않았기 때문에 실제로 유지 관리의 악몽으로 변할 것 입니다. 대신 우리는 변화하는 클라이언트 요구 사항에 실제로 적합한 단일 모션 구현을 만들려고 노력하고 있었으며 종종 모든 구현자가 IMotion동일한 구현 및 상태를 사용 하도록 강제하는 순수한 인터페이스 아이디어를 많이 사용하여 노력했습니다. 중복 목표.

따라서 인터페이스 Behaviors는 엔터티와 더 광범위하게 연결 되었습니다 . IMotion간단하게 될 것 Motion"구성 요소"(나는 우리가 더 가까이 일반적인 정의하는 "완전한"엔티티를 구성하는 조각의 하나 멀리 COM에서 "구성 요소"에 정의 된 방식을 변경).

이 대신에 :

class IMotion
{
public:
    virtual ~IMotion() {}
    virtual void transform(const Matrix& mat) = 0;
    ...
};

우리는 이것을 다음과 같이 발전 시켰습니다 :

class Motion
{
public:
    void transform(const Matrix& mat)
    {
        ...
    }
    ...

private:
    Matrix transformation;
    ...
};

이것은 추상에서 콘크리트로 되돌아 가기 시작하는 의존성 역전 원칙에 대한 명백한 위반이지만, 그런 추상화 수준은 우리가 합리적인 의심을 넘어서서 미래의 진정한 필요를 예측할 수있는 경우에만 유용합니다 이러한 유연성을 위해 어리석은 "만약"시나리오를 사용자 경험에서 완전히 분리했습니다 (어쨌든 디자인 변경이 필요할 수 있음).

그래서 우리는이 디자인으로 진화하기 시작했습니다. QueryInterface더 좋아 QueryBehavior졌다. 또한 여기서 상속을 사용하는 것은 무의미 해 보였습니다. 대신 컴포지션을 사용했습니다. 개체는 런타임에 가용성을 쿼리하고 주입 할 수있는 구성 요소 모음으로 바뀌 었습니다.

여기에 이미지 설명을 입력하십시오

일부 전문가 :

  • 이전의 순수한 인터페이스 COM 스타일 시스템보다 여전히 우리의 경우에 유지하기가 훨씬 쉬웠습니다. 요구 사항 변경이나 워크 플로 불만과 같은 예기치 않은 놀라움은 매우 중앙적이고 명백한 Motion구현을 통해보다 쉽게 ​​수용 할 수 있으며 백 가지 하위 유형에 분산되지 않습니다.
  • 실제로 필요한 새로운 차원의 유연성을 부여했습니다. 이전 시스템에서 상속은 정적 관계를 모델링하기 때문에 C ++에서 컴파일 타임에 새로운 엔티티 만 효과적으로 정의 할 수있었습니다. 스크립팅 언어로는 할 수 없었습니다. 예를 들어 컴포지션 접근 방식을 사용하면 구성 요소를 구성 요소에 연결하고 목록에 추가하는 것만으로 런타임에 새로운 개체를 즉시 묶을 수 있습니다. "엔티티 (entity)"는 빈 캔버스로 바뀌어 우리가 필요로하는 모든 것의 콜라주를 함께 모을 수있게되었으며, 관련 시스템이 자동으로 이러한 엔티티를 인식하고 처리합니다.

몇 가지 단점 :

  • 우리는 여전히 효율성 부서에서 어려움을 겪고 있었으며 성능이 중요한 영역에서 유지 관리가 쉬웠습니다. 각 시스템은 여전히 ​​이러한 동작을 제공하는 엔터티의 구성 요소를 캐시하여 반복적으로 반복하고 사용 가능한 것을 확인하지 않기를 원할 것입니다. 성능을 요구하는 각 시스템은 약간 다른 방식으로이 작업을 수행하며,이 캐시 된 목록 및 데이터 구조 (일부 검색 형식이 절두체 컬링 또는 레이트 레이싱과 같은 경우)를 업데이트하지 못하는 경우 다른 버그가 발생하기 쉽습니다. 불명확 한 장면 변경 이벤트, 예.
  • 이 세밀한 작은 행동, 간단한 물체와 관련하여 손가락을 넣을 수없는 어색하고 복잡한 것이 여전히있었습니다. 우리는 여전히 필요했던 이러한 "행동"객체들 사이의 상호 작용을 다루기 위해 많은 이벤트를 낳았으며 결과는 매우 분산 된 코드였습니다. 각각의 작은 물체는 정확성을 테스트하기 쉬웠으며 개별적으로 찍은 경우 종종 완벽하게 정확했습니다. 그러나 우리는 여전히 작은 마을로 구성된 거대한 생태계를 유지하려고 노력하고 있으며, 그들이 모두 개별적으로하는 일에 대해 추론하여 전체적으로 만들기 위해 노력하고있는 것처럼 느껴졌습니다. C- 스타일 80 년대 코드베이스는 하나의 서사시, 과잉 인구, 거대 도시인 것 같았습니다.
  • 추상화가 부족하여 유연성이 떨어지지 만 실제로는 실제로 필요한 적이 없었기 때문에 실질적인 단점은 거의 없습니다 (적어도 이론적 인 것은 아니지만).
  • ABI 호환성을 유지하는 것은 항상 어려웠으며, 이는 "동작"과 관련된 안정적인 인터페이스뿐만 아니라 안정적인 데이터를 요구함으로써 더욱 어려워졌습니다. 그러나 상태 변경이 필요한 경우 새로운 비헤이비어를 쉽게 추가하고 기존 비헤이비어를 더 이상 사용할 수 없으며 하위 유형 수준의 인터페이스 아래에서 백플 래핑을 수행하여 버전 관리 문제를 처리하는 것보다 쉽습니다.

발생한 한 가지 현상은 이러한 행동 구성 요소에 대한 추상화를 잃어 버렸기 때문에 더 많은 기능을 가지고 있다는 것입니다. 예를 들어, 추상 IRenderable컴포넌트 대신 콘크리트 Mesh또는 PointSprites컴포넌트로 객체를 부착합니다 . 렌더링 시스템은 렌더링 방법 MeshPointSprites구성 요소 를 알고 그러한 구성 요소를 제공하고 그 요소를 그리는 엔티티를 찾습니다. 다른 경우에, 우리는 우리가 후시 SceneLabel에서 필요하다는 것을 발견 한 것처럼 기타 렌더러 블을 가졌기 때문에 SceneLabel그러한 경우에 관련 엔티티에 (를 포함하여 Mesh) 첨부 할 것 입니다. 그런 다음 렌더링 시스템 구현을 업데이트하여 해당 항목을 제공하는 엔티티를 렌더링하는 방법을 알았으며 변경하기가 매우 쉽습니다.

이 경우 구성 요소로 구성된 엔터티를 다른 엔터티의 구성 요소로 사용할 수도 있습니다. 우리는 레고 블록을 연결하여 그런 식으로 물건을 쌓았습니다.

ECS : 시스템 및 원시 데이터 구성 요소

그 마지막 시스템은 내가 스스로 만들었을 때까지 우리는 여전히 COM으로 헛소리하고있었습니다. 엔터티 구성 요소 시스템이되고 싶었지만 당시에는 익숙하지 않았습니다. 건축 영감을 얻기 위해 AAA 게임 엔진을 보았을 때 필자의 분야를 포화시킨 COM 스타일 예제를 둘러보고있었습니다. 나는 마침내 그 일을 시작했다.

내가 빠진 것은 몇 가지 핵심 아이디어였습니다.

  1. "구성 요소"를 처리하기위한 "시스템"의 공식화.
  2. "컴포넌트"는 더 큰 객체로 구성된 행동 객체가 아닌 원시 데이터입니다.
  3. 구성 요소 모음과 관련된 엄격한 ID에 지나지 않습니다.

마침내 그 회사를 떠나 ECS를 인디로 일하기 시작했습니다 (여전히 저축을하는 동안 계속 노력하고 있습니다). 지금까지 가장 관리하기 쉬운 시스템이었습니다.

ECS 접근 방식에서 내가 주목 한 것은 여전히 ​​위의 문제로 해결되었습니다. 가장 중요한 것은 복잡한 상호 작용을하는 작은 마을 대신 건강한 도시를 관리하는 것처럼 느껴졌습니다. 효율적으로 관리하기에는 인구 규모가 너무 큰 모 놀리 식 "거대 도시"만큼 유지하기가 쉽지 않았지만 무역 경로에 대해 생각하는 작은 마을들이 서로 상호 작용하는 세계만큼 혼란 스럽지는 않았습니다. 그들 사이에 악몽의 그래프가 형성되었습니다. ECS는 렌더링 시스템과 같이 부피가 큰 "시스템"에 대한 모든 복잡성을 증류 시켰습니다.

원시 데이터가 된 구성 요소 는 처음에는 OOP의 기본 정보 숨기기 원칙조차도 깨뜨리기 때문에 정말 이상하게 느껴졌습니다 . 캡슐화 및 정보 숨기기가 필요한 불변량을 유지하는 기능이었던 OOP에 대해 내가 가장 큰 가치 중 하나에 도전하는 것은 일종의 도전이었습니다. 그러나 수십 개나 그 이상의 광범위한 시스템에서 발생하는 상황이 신속하게 명백 해짐에 따라 이러한 로직 대신 수백 개에서 수천 개의 하위 유형에 걸쳐 분산 된 인터페이스를 구현하는 데이터 대신 해당 데이터를 변환합니다. 시스템이 데이터에 액세스하는 기능 및 구현을 제공하고 구성 요소가 데이터를 제공하며 엔터티가 구성 요소를 제공하는 경우를 제외하고는 여전히 OOP 스타일 방식으로 생각하는 경향이 있습니다.

데이터를 광범위하게 변환하는 소수의 부피가 큰 시스템이 있었을 때 시스템으로 인한 부작용에 대해 추론 하기쉬워 졌습니다. 시스템은 훨씬 더 "더 평평 해졌다"고, 호출 스택은 각 스레드마다 그 어느 때보 다 얕아졌다. 나는 감독자 수준에서 시스템에 대해 생각할 수 있었고 이상한 놀라움에 빠지지 않았습니다.

마찬가지로, 이러한 쿼리 제거와 관련하여 성능이 중요한 영역도 간단 해졌습니다. "시스템"이라는 개념이 공식화되면서 시스템은 관심있는 구성 요소를 구독하고 해당 기준을 충족하는 캐시 된 엔터티 목록을 전달할 수 있습니다. 각각의 개인은 그 캐싱 최적화를 관리 할 필요가 없었으며, 한 곳으로 집중되었습니다.

일부 전문가 :

  • 예상치 못한 요구를 겪을 때 디자인 코너에 갇히지 않고 경력에서 겪었던 거의 모든 주요 건축 문제를 해결하려고합니다.

몇 가지 단점 :

  • 나는 때때로 때때로 내 머리를 감싸는 데 어려움을 겪고 있으며 사람들이 그것이 무엇을 의미하는지, 어떻게 행동하는지에 대해 논쟁하는 게임 산업 내에서도 가장 성숙하거나 잘 확립 된 패러다임이 아닙니다. 내가 일했던 이전 팀에서 할 수있는 일은 아닙니다. COM 스타일 사고 방식이나 원래 코드베이스의 1980 년대 C 스타일 사고 방식에 깊이 빠져있는 멤버들로 구성되어 있습니다. 때때로 혼란스러워하는 부분은 구성 요소 간의 그래프 스타일 관계를 모델링하는 방법과 같지만 나중에 다른 구성 요소에 종속적으로 구성 요소를 만들 수있는 나중에 나중에 끔찍한 해결책을 찾지 못했습니다. 구성 요소는 부모로서 다른 하나에 의존하며 시스템은 동일한 재귀 모션 계산을 반복적으로 수행하지 않기 위해 메모를 사용합니다. "
  • ABI는 여전히 어렵지만 지금까지는 순수한 인터페이스 접근 방식보다 쉽다고 말하기까지했습니다. 데이터 안정성은 인터페이스 안정성보다는 ABI의 유일한 초점이되며 인터페이스 안정성보다 데이터 안정성을 달성하는 것이 더 쉽습니다 (예 : 새로운 매개 변수가 필요하기 때문에 기능을 변경하려는 유혹은 없습니다). 이런 종류의 일은 ABI를 깨뜨리지 않는 거친 시스템 구현에서 발생합니다).

여기에 이미지 설명을 입력하십시오

그러나 게임 엔진에서 공통적 인 Component-Entity-System 아키텍처를 사용하여 응용 프로그램을 만드는 것도 합리적입니까?

어쨌든, 나는 개인적으로 VFX 예제가 강력한 후보라고 절대적으로 "예"라고 말할 것입니다. 그러나 그것은 여전히 ​​게임의 요구와 상당히 유사합니다.

게임 엔진의 관심사와 완전히 분리 된 더 먼 지역에서 연습하지는 않았지만 (VFX는 매우 유사합니다) 훨씬 더 많은 지역이 ECS 접근의 좋은 후보 인 것처럼 보입니다. 아마도 GUI 시스템조차도 하나에 적합 할 수도 있지만, 여전히 더 많은 OOP 접근 방식을 사용합니다 (그러나 Qt와 달리 깊은 상속은 없습니다).

그것은 널리 탐험되지 않은 영토이지만, 당신의 실체가 풍부한 "특성"의 조합으로 구성 될 수있을 때마다 (그리고 어떤 특성의 조합이 변경 될 수 있는지), 그리고 당신이 소수의 일반화 된 곳이 있다면 나에게 적합 해 보입니다. 필요한 특성을 가진 개체를 처리하는 시스템.

다중 상속 또는 개념 에뮬레이션 (예 : 믹스 인)과 같은 것을 사용하여 깊은 상속 계층 구조 또는 수백 개의 콤보로 수백 개 이상의 콤보를 생성하려는 유혹을받는 시나리오에 대한 실질적인 대안이됩니다. 특정 인터페이스의 콤보를 구현하지만 시스템 수가 적은 (예 : 수십 개) 플랫 계층 구조의 클래스

이 경우 코드베이스의 복잡성은 유형 조합 수가 아닌 시스템 수에 비례하기 시작합니다. 각 유형은 이제 원시 데이터에 지나지 않는 구성 요소를 구성하는 엔터티에 불과하기 때문입니다. GUI 시스템은 이러한 기본 사양에 적합하며 다른 기본 유형 또는 인터페이스와 결합 된 수백 가지의 위젯 유형이있을 수 있지만 소수의 시스템 (레이아웃 시스템, 렌더링 시스템 등) 만 처리 할 수 ​​있습니다. GUI 시스템이 ECS를 사용했다면 상속 된 인터페이스 나 기본 클래스가있는 수백 가지의 다른 객체 유형 대신이 기능 중 일부가 이러한 기능을 모두 제공 할 때 시스템의 정확성에 대해 추론하기가 훨씬 쉬울 것입니다. GUI 시스템이 ECS를 사용하는 경우 위젯에는 기능이없고 데이터 만 있습니다. 위젯 엔티티를 처리하는 소수의 시스템 만이 기능을 갖습니다. 위젯에 대한 재정의 가능한 이벤트를 처리하는 방법은 저를 넘어서지 만 지금까지 제한된 경험을 바탕으로 해당 유형의 논리를 주어진 시스템으로 중앙 집중식으로 전송할 수없는 경우를 찾지 못했습니다. 가늠자는 내가 기대했던 것보다 훨씬 더 우아한 솔루션을 산출했습니다.

나는 그것이 생명의 은인 이었기 때문에 더 많은 분야에서 사용되는 것을보고 싶습니다. 물론 설계가 구성 요소를 집계하는 엔터티에서 해당 구성 요소를 처리하는 거친 시스템에 이르기까지 이러한 방식으로 분류되지 않으면 적합하지 않습니다. .


1) 예제 VFX 프로그램은 사용자 관점에서 무엇을 했습니까? 2) 현재 어떤 ECS 프로젝트를 진행하고 있습니까? ♥ 이것을 작성해 주셔서 감사합니다! ♥
강아지

1
매우 철저한 설명-감사합니다. 적용 가능한 ECS가 게임을 넘어서는 방법과 관련하여 동일한 결론에 도달하고있는 것 같습니다. 제 경우에는 특히 복잡한 GUI입니다. 처음에는 일반적으로 수행되는 작업 (UI 프레임 워크에서 특히 두드러지는 깊은 상속 계층 구조)에 반하는 것이 정말 이상하게 느껴지 지만,이 방법을 찾는 다른 사람들이 더 효과적이라는 사실은 가슴이 뜨겁습니다.
Danny Yaroslavski

1
이 위대한 ... 기사 감사합니다! 컴포넌트 기반 GUI의 경우 Unity3d의 UGUI를 살펴 보는 것이 좋습니다. CocoaTouch와 같은 상속 기반 제품에 비해 매우 유연하고 확장 가능합니다.
Ivan Mir

16

게임 엔진의 Component-Entity-System 아키텍처는 게임 소프트웨어의 특성과 고유 한 특성 및 품질 요구 사항으로 인해 게임에 적합합니다. 예를 들어, 엔티티는 게임의 사물을 다루고 작업하는 균일 한 수단을 제공하는데, 이는 목적과 용도가 크게 다를 수 있지만 시스템에 의해 균일 한 방식으로 렌더링, 업데이트 또는 직렬화 / 직렬화되어야합니다. 구성 요소 모델을이 아키텍처에 통합하면 간단한 코드 구조를 유지하면서 코드 결합이 적어 필요에 따라 더 많은 기능을 추가 할 수 있습니다. CAD 응용 프로그램, A / V 코덱, 기타 응용 프로그램 등이 디자인의 특성을 활용할 수있는 다양한 소프트웨어 시스템이 있습니다.

TL; DR-디자인 패턴은 문제 영역이 디자인에 부과하는 기능과 단점에 충분히 적합한 경우에만 잘 작동합니다.


8

문제 영역이 잘 맞다면.

현재 진행중인 작업에는 다양한 런타임 요소에 따라 다양한 기능을 지원해야하는 앱이 있습니다. 구성 요소 기반 엔터티를 사용하여 이러한 기능을 모두 분리하고 확장 성과 테스트 가능성을 단독으로 허용하는 것은 목가적이었습니다.

편집 : 내 작업에는 독점 하드웨어 (C #)에 대한 연결을 제공하는 것이 포함됩니다. 하드웨어가 어떤 폼 팩터인지, 어떤 펌웨어가 설치되어 있는지, 클라이언트가 구입 한 서비스 수준 등에 따라 장치에 다른 수준의 기능을 제공해야합니다. 인터페이스가 동일한 일부 기능조차도 장치 버전에 따라 구현이 다릅니다.

여기의 이전 코드베이스에는 구현되지 않은 인터페이스가 매우 광범위했습니다. 일부는 여러 가지 얇은 인터페이스를 가지고 있었으며 하나의 짐승 클래스에서 정적으로 구성되었습니다. 일부는 단순히 문자열-> 문자열 사전을 사용하여 모델링했습니다. (우리는 모두 더 잘 할 수 있다고 생각하는 많은 부서가 있습니다)

이들은 모두 결함이 있습니다. 넓은 인터페이스는 효과적으로 모의 / 테스트하기위한 반입니다. 새로운 기능을 추가한다는 것은 공개 인터페이스 (및 기존의 모든 구현)를 변경하는 것을 의미합니다. 많은 얇은 인터페이스는 코드를 매우 못 생겼지 만 결국 큰 뚱뚱한 객체 테스트를 통과했지만 여전히 어려움을 겪었습니다. 또한 얇은 인터페이스는 종속성을 잘 관리하지 못했습니다. 문자열 사전에는 일반적인 구문 분석 및 존재 문제뿐만 아니라 성능, 가독성 및 유지 관리 성 문제가 있습니다.

우리가 지금 사용하는 것은 런타임 정보를 기반으로 구성 요소를 발견하고 구성한 매우 얇은 엔티티입니다. 의존성은 핵심 컴포넌트 프레임 워크에 의해 선언적이고 자동 해결됩니다. 구성 요소 자체는 종속성과 직접 작동하기 때문에 격리 된 상태에서 테스트 할 수 있으며, 종속성이없는 문제는 종속성을 처음 사용하는 것이 아니라 초기 및 한 위치에서 발견됩니다. 새로운 (또는 테스트) 구성 요소를 제거 할 수 있으며 기존 코드의 영향을받지 않습니다. 소비자는 엔티티에 구성 요소에 대한 인터페이스를 요청하므로 다양한 구현 (및 구현이 런타임 데이터에 매핑되는 방법)을 자유롭게 선택할 수 있습니다.

객체와 인터페이스의 구성에 공통 구성 요소의 일부 (매우 다양한) 하위 집합이 포함될 수있는 상황에서는 매우 효과적입니다.


1
허용한다고 가정하면 현재 작업에 대한 자세한 정보를 제공 할 수 있습니까? CES가 당신이 짓고있는 것에 목가적 인 방법을 알고 싶습니다.
앤드류 드 안드레이드

당신의 경험에 관한 기사, 종이 또는 블로그가 있습니까? 또한, 나는 :) 그것에 대해 더 기술적 인 내용을 가지고 싶습니다
user1778770

@ user1778770-공개되어 있지 않습니다. 어떤 종류의 질문이 있었습니까?
Telastyn

글쎄, 간단한 것부터 시작하자. 컨셉이 전체 애플리케이션 스택 (예 : 비즈니스에서 프론트 엔드까지)에 걸쳐 있습니까? 아니면 단일 사용 사례의 단일 계층입니까?
user1778770

@ user1778770-구현에서 엔티티 / 구성 요소는 한 계층에 존재합니다. 다른 엔티티는 다른 계층에 존재할 수 있지만, 종종 1 : 1이 아닙니다 (또는 다른 계층은 이점을 제공하지 않습니다).
Telastyn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.