외부 구성 요소 관리자와 함께 엔티티 시스템을 구성합니까?


13

탑 다운 멀티 플레이어 2D 슈팅 게임용 게임 엔진을 디자인하고 있는데, 다른 탑 다운 슈팅 게임에도 합리적으로 재사용 할 수 있기를 원합니다. 현재 엔터티 시스템과 같은 것을 어떻게 디자인해야하는지 생각하고 있습니다. 먼저 나는 이것에 대해 생각했다.

EntityManager라는 클래스가 있습니다. Update라는 메소드와 Draw라는 다른 메소드를 구현해야합니다. 논리와 렌더링을 분리하는 이유는 독립형 서버를 실행하는 경우 Draw 메소드를 생략 할 수 있기 때문입니다.

EntityManager는 BaseEntity 유형의 오브젝트 목록을 소유합니다. 각 엔터티는 EntityModel (엔터티의 드로어 블 표현), EntityNetworkInterface 및 EntityPhysicalBody와 같은 구성 요소 목록을 소유합니다.

EntityManager는 EntityRenderManager, EntityNetworkManager 및 EntityPhysicsManager와 같은 구성 요소 관리자 목록도 소유합니다. 각 구성 요소 관리자는 엔터티 구성 요소에 대한 참조를 유지합니다. 이 코드를 엔티티 자체 클래스에서 다른 그룹으로 옮기는 데는 여러 가지 이유가 있습니다. 예를 들어 게임에 외부 물리 라이브러리 인 Box2D를 사용하고 있습니다. Box2D에서 먼저 몸체와 모양을 월드에 추가하고 (이 경우에는 EntityPhysicsManager가 소유) 충돌 콜백을 추가합니다 (시스템의 엔티티 오브젝트 자체에 전달됨). 그런 다음 시스템의 모든 것을 시뮬레이션하는 기능을 실행하십시오. 이와 같은 외부 구성 요소 관리자에서 수행하는 것보다 더 나은 솔루션을 찾는 것이 어렵다는 것을 알았습니다.

엔티티 작성은 다음과 같이 수행됩니다. EntityManager 는 해당 클래스의 엔티티 작성 방법 을 등록 하는 RegisterEntity (entityClass, factory) 메소드를 구현합니다 . 또한 BaseEntity 유형의 오브젝트를 리턴하는 CreateEntity (entityClass) 메소드를 구현합니다.

이제 내 문제가 발생합니다. 구성 요소에 대한 참조가 구성 요소 관리자에 어떻게 등록됩니까? 팩토리 / 클로저에서 구성 요소 관리자를 참조하는 방법을 모르겠습니다.


아마도 이것이 하이브리드 시스템인지는 모르겠지만 "관리자"가 일반적으로 "시스템"이라고 칭하는 것 같습니다. 즉, 엔티티는 추상 ID입니다. 컴포넌트는 데이터 풀입니다. "관리자"라고하는 것은 일반적으로 "시스템"이라고하는 것입니다. 어휘를 올바르게 해석하고 있습니까?
BRPocock

내 질문은 여기에 관심이있을 수 있습니다 게임 구성 요소, 게임 관리자 및 개체 속성
조지 Duckett

gamadu.com/artemis를 살펴보고 해당 방법이 귀하의 질문에 답변되는지 확인하십시오.
Patrick Hughes

1
정의에 대한 합의가 거의 없기 때문에 엔터티 시스템을 설계하는 방법은 없습니다. @BRPocock이 설명하고 Artemis가 사용하는 내용은 t-machine.org/index.php/category/entity-systems 위키와 함께 자세히 설명되어 있습니다 . entity-systems.wikidot.com
user8363

답변:


6

시스템은 사용되는 언어에 따라 일종의 맵, 사전 객체 또는 연관 배열에 Entity to Component의 키 값 쌍을 저장해야합니다. 또한 엔터티 개체를 만들 때 시스템에서 등록을 해제 할 필요가 없으면 관리자에 저장하는 것에 대해 걱정하지 않아도됩니다. 엔터티는 구성 요소의 합성이지만 구성 요소 업데이트를 처리하지 않아야합니다. 시스템에서 처리해야합니다. 대신 Entity를 시스템에 포함 된 모든 구성 요소에 매핑 된 키와 해당 구성 요소가 서로 통신 할 수있는 통신 허브로 취급하십시오.

Entity-Component-System 모델의 큰 부분은 한 구성 요소에서 다른 구성 요소 구성 요소로 메시지를 전달하는 기능을 매우 쉽게 구현할 수 있다는 것입니다. 이를 통해 구성 요소는 해당 구성 요소가 누구인지 또는 변경되는 구성 요소를 처리하는 방법을 실제로 모른 채 다른 구성 요소와 대화 할 수 있습니다. 대신 메시지를 전달하고 구성 요소 자체를 변경하도록합니다 (있는 경우).

예를 들어, 위치 시스템에는 코드가 많지 않고 위치 구성 요소에 매핑 된 개체 개체 만 추적합니다. 그러나 위치가 변경되면 관련 엔터티에 메시지를 보내면 해당 엔터티의 모든 구성 요소로 전달됩니다. 어떤 이유에서든 입장이 바뀝니 까? 위치 시스템은 위치가 변경되었다는 메시지를 Entity에게 보내고 어딘가에서 해당 엔티티의 이미지 렌더링 구성 요소가 해당 메시지를 가져와 다음에 그 자체로 그릴 위치를 업데이트합니다.

반대로 물리 시스템은 모든 물체가 무엇을하고 있는지 알아야합니다. 충돌을 테스트하기 위해 모든 월드 오브젝트를 볼 수 있어야합니다. 충돌이 발생하면 엔터티의 구성 요소를 직접 참조하는 대신 일종의 "방향 변경 메시지"를 엔터티에 보내 엔터티의 방향 구성 요소를 업데이트합니다. 이것은 관리자가 존재하는 특정 구성 요소에 의존하는 대신 메시지를 사용하여 방향을 변경하는 방법을 알아야 할 필요성을 분리시킵니다. 예상 개체가 없기 때문에 발생).

네트워크 인터페이스가 있다고 언급했기 때문에 이것으로부터 큰 이점을 알 수 있습니다. 네트워크 구성 요소는 다른 사람이 알아야 할 모든 메시지를 듣습니다. 가십을 좋아합니다. 그런 다음 네트워크 시스템이 업데이트되면 네트워크 구성 요소는 해당 메시지를 다른 클라이언트 시스템의 다른 네트워크 시스템으로 전송 한 다음 해당 메시지를 다른 모든 구성 요소로 다시 보내서 플레이어 위치 등을 업데이트합니다. 특정 엔티티 만 가능하도록 특별한 논리가 필요할 수 있습니다. 네트워크를 통해 메시지를 보내지 만 이것이 시스템의 아름다움입니다. 올바른 것을 등록하여 해당 논리를 제어 할 수 있습니다.

한마디로 :

엔터티는 메시지를받을 수있는 구성 요소의 구성입니다. 엔터티는 메시지를 수신하여 해당 메시지를 모든 구성 요소에 위임하여 메시지를 업데이트 할 수 있습니다. (위치 변경 메시지, 속도 변경 방향 등) 모든 구성 요소가 서로 직접 대화하지 않고 서로들을 수있는 중앙 사서함과 같습니다.

컴포넌트는 엔티티의 일부 상태를 저장하는 엔티티의 작은 부분입니다. 이들은 특정 메시지를 구문 분석하고 다른 메시지를 버릴 수 있습니다. 예를 들어 "방향 구성 요소"는 "방향 변경 메시지"에만 관심이 있고 "위치 변경 메시지"에는 관심이 없습니다. 구성 요소는 메시지를 기반으로 자체 상태를 업데이트 한 다음 시스템에서 메시지를 보내 다른 구성 요소의 상태를 업데이트합니다.

시스템은 특정 유형의 모든 구성 요소를 관리하며 각 프레임마다 해당 구성 요소를 업데이트하고 해당 구성 요소가 관리하는 구성 요소의 메시지를 구성 요소가 속한 엔터티로 발송합니다.

시스템은 모든 구성 요소를 병렬로 업데이트하고 모든 메시지를 원하는대로 저장할 수 있습니다. 그런 다음 모든 시스템의 업데이트 메소드 실행이 완료되면 각 시스템에 특정 순서로 메시지를 발송하도록 요청합니다. 먼저 제어, 물리, 방향, 위치, 렌더링 등이 제어됩니다. 물리 방향 변경이 항상 제어 기반 방향 변경의 무게를 초과해야하기 때문에 제어 순서가 전달되는 순서가 중요합니다.

도움이 되었기를 바랍니다. 그것은 디자인 패턴의 지옥이지만, 올바르게 끝나면 엄청나게 강력합니다.


0

엔진에서 비슷한 시스템을 사용하고 있으며 각 엔터티에 구성 요소 목록이 포함되어 있습니다. EntityManager에서 각 엔티티를 쿼리하고 주어진 컴포넌트를 포함하는 엔티티를 확인할 수 있습니다. 예:

class Component
{
    private uint ID;
    // etc...
}

class Entity
{
    List<Component> Components;
    // etc...
    public bool Contains(Type type)
    {
        foreach(Component comp in Components)
        {
            if(typeof(comp) == type)
                return true;
        }
        return false;
    }
}

class EntityManager
{
    List<Entity> Entities;
    // etc...
    public List<Entity> GetEntitiesOfType(Type type)
    {
        List<Entity> results = new List<Entity>();
        foreach(Entity entity in Entities)
        {
            if(entity.Contains(type))
                results.Add(entity);
        }
        return results;
    }
}

분명히 이것은 정확한 코드는 아니지만 (실제로 사용하지 않고 다른 구성 요소 유형을 확인하기 위해 템플릿 함수가 필요 typeof하지만) 개념이 있습니다. 그런 다음 해당 결과를보고 찾고있는 구성 요소를 참조한 후 공장에 등록 할 수 있습니다. 이렇게하면 구성 요소와 해당 관리자 간의 직접적인 연결이 방지됩니다.


3
주의 사항 : 개체에 데이터가 포함 된 시점에서 개체가 아니라 개체입니다.이 구조에서 ECS의 병렬화 (sic?) 이점을 대부분 상실합니다. "순수한"E / C / S 시스템은 관계형이며 객체 지향적이지 않습니다. 어떤 경우에는 반드시 "나쁜"것이 아니라 확실히 "관계형 모델을 깨는 것"
BRPocock

2
나는 당신을 이해하지 못합니다. 내 이해는 (그리고 내가 틀렸다면 수정하십시오) 기본 Entity-Component-System에는 Components를 포함하고 ID, 이름 또는 일부 식별자가있을 수있는 Entity 클래스가 있습니다. 엔터티의 "유형"의 의미에 대한 오해가있을 수 있습니다. 엔터티 "유형"이라고 말하면 구성 요소 유형을 나타냅니다. 즉, 엔티티에 Sprite Component가 포함 된 경우 "Sprite"유형입니다.
Mike Cluck

1
순수한 엔티티 / 컴포넌트 시스템에서 엔티티는 일반적으로 원자 적입니다 typedef long long int Entity. Component는 struct첨부 된 Entity에 대한 참조 가있는 레코드 (객체 클래스로 구현되거나 단지 ) 일 수 있습니다. 시스템은 방법 또는 방법 모음입니다. ECS 모델은 OOP 모델과 매우 호환되지 않지만 Component는 (대부분의) 데이터 전용 Object 일 수 있으며, 시스템은 상태가 구성 요소에있는 코드 전용 Singleton Object 일 수 있지만 "하이브리드"시스템은 "순수한"것보다 더 일반적으로, 그들은 많은 타고난 이익을 잃습니다.
BRPocock

2
@BRPocock re "순수한"엔터티 시스템. 나는 개체로서의 엔티티가 완벽하게 훌륭하다고 생각합니다. 단순한 ID 일 필요는 없습니다. 하나는 직렬화 된 표현이고 다른 하나는 객체 / 개념 / 엔티티의 메모리 내 레이아웃입니다. 데이터 중심성을 유지할 수있는 한, "순수한"방식이기 때문에 비 아이디어적인 코드에 묶여서는 안됩니다.
Raine

1
@BRPocock 이것은 "t-machine"과 같은 엔터티 시스템에 대한 공정한 경고입니다. 이유를 이해하지만 이것이 컴포넌트 기반 엔티티를 모델링하는 유일한 방법은 아닙니다. 배우는 흥미로운 대안입니다. 나는 순수하게 논리적 인 엔터티에 대해 더 감사하는 경향이 있습니다.
Raine

0

1) 팩토리 메소드에는 그것을 호출 한 EntityManager에 대한 참조가 전달되어야합니다 (예를 들어 C #을 사용하겠습니다).

delegate BaseEntity EntityFactory(EntityManager manager);

2) CreateEntity가 엔티티의 클래스 / 유형 외에도 ID (예 : 문자열, 정수, 사용자에게 달려 있음)를 받고 해당 ID를 키로 사용하여 사전에 작성된 엔티티를 자동으로 등록하십시오.

class EntityManager
{
    // Rest of class omitted

    BaseEntity CreateEntity(string id, Type entityClass)
    {
        BaseEntity entity = factories[entityClass](this);
        registry.Add(id, entity);
        return entity;
    }

    Dictionary<Id, BaseEntity> registry;
}

3) ID로 엔티티를 얻으려면 EntityManager에 Getter를 추가하십시오.

class EntityManager
{
    // Rest of class omitted

    BaseEntity GetEntity(string id)
    {
        return registry[id];
    }
}

그리고 이것이 Factory 메소드 내에서 ComponentManager를 참조하는 데 필요한 전부입니다. 예를 들어 :

BaseEntity CreateSomeSortOfEntity(EntityManager manager)
{
    // Create and configure entity
    BaseEntity entity = new BaseEntity();
    RenderComponent renderComponent = new RenderComponent();
    entity.AddComponent(renderComponent);

    // Get a reference to the render manager and register component
    RenderEntityManager renderer = manager.GetEntity("RenderEntityManager") as RenderEntityManager;
    if(renderer != null)
        renderer.Register(renderComponent)

    return entity;
}

ID 외에도 일종의 Type 속성 (사용자 정의 열거 형 또는 언어의 유형 시스템에 의존)을 사용하고 특정 유형의 모든 BaseEntities를 반환하는 게터를 만들 수 있습니다.


1
독창적이지 않고 다시 ... 순수한 실체 (관계) 시스템에서, 실체는 구성 요소에 의해 부여 된 것을 제외하고는 유형이 없다…
BRPocock

@BRPocock : 순수한 미덕을 따르는 예를 만들 수 있습니까?
Zolomon

1
@Raine 아마도, 나는 이것에 대한 직접적인 경험이 없지만, 그것이 내가 읽은 것입니다. 그리고 id별로 구성 요소를 찾는 데 소요되는 시간을 줄이기 위해 구현할 수있는 최적화가 있습니다. 캐시 일관성에 관해서는, 특히 구성 요소가 가볍거나 단순한 속성 일 때 동일한 유형의 데이터를 메모리에 연속적으로 저장하기 때문에 의미가 있다고 생각합니다. PS3의 단일 캐시 누락은 수천 개의 CPU 명령만큼 비쌀 수 있으며 유사한 유형의 데이터를 연속적으로 저장하는 이러한 접근 방식은 현대 게임 개발에서 매우 일반적인 최적화 기술입니다.
David Gouveia

2
참고로 : "순수한"엔터티 시스템 : 엔터티 ID는 일반적으로 다음과 같습니다 typedef unsigned long long int EntityID;.; 이상적으로는 각 시스템이 별도의 CPU 또는 호스트에있을 수 있으며 해당 시스템에서 관련되거나 활성 인 구성 요소 만 가져와야한다는 것입니다. Entity 객체를 사용하면 각 호스트에서 동일한 Entity 객체를 인스턴스화해야하므로 확장이 더 어려워집니다. 순수한 엔티티 구성 요소 시스템 모델은 일반적으로 엔티티가 아닌 시스템별로 노드 (프로세스, CPU 또는 호스트)에서 처리를 분할합니다.
BRPocock

1
@DavidGouveia는“최적화… ID로 엔티티 조회”를 언급했습니다. 사실, 내가 이런 식으로 구현 한 (몇 가지) 시스템은 그렇게하지 않는 경향이 있습니다. 더 자주, 컴포넌트 간 조인에만 엔티티 (ID)를 사용하여 특정 시스템에 관심이 있음을 나타내는 패턴으로 컴포넌트를 선택하십시오.
BRPocock
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.