구성 요소가 많은 OOP 대 순수 엔티티 구성 요소 시스템? [닫은]


13

나는 남용하고 심지어 상속 재산을 남용하는 죄를 지었다는 것을 인정한다. OOP 과정을 수강 할 때 처음으로 만든 (텍스트) 게임 프로젝트는 "문"과 "문이있는 방", "2 개의 문이있는 방"에서 "잠금 문"및 "잠금 해제 문"까지 진행되었으며 "방"에서 등등.

최근에 작업 한 (그래픽) 게임을 통해 수업을 배우고 상속 사용을 제한한다고 생각했습니다. 그러나 문제가 곧 나타나기 시작했습니다. 루트 클래스가 점점 부풀어 오르기 시작했고 리프 클래스에는 중복 코드가 가득했습니다.

나는 여전히 잘못하고 있다고 생각했으며 온라인에서 찾은 후에 내가이 문제를 가진 유일한 사람이 아니라는 것을 알았습니다. 철저한 조사를 거친 후 엔터티 시스템을 발견 한 방법입니다 (읽기 : googlefu)

읽었을 때 구성 요소가있는 전통적인 OOP 계층 구조에서 발생하는 문제를 얼마나 명확하게 해결할 수 있는지 알 수있었습니다. 그러나 이것은 첫 번째 독서에있었습니다. 내가 더 우연히 만났을 때 ... T-machine 과 같은 "급진적 인"ES 접근 .

나는 그들이 사용하고있는 방법에 동의하지 않기 시작했다. 순수한 구성 요소 시스템은 과도하거나 직관적이지 않은 것으로 보였으며 이는 아마도 OOP의 강점입니다. 저자는 ES 시스템이 OOP의 반대라고 말하면서 OOP와 함께 사용할 수는 있지만 실제로는 안됩니다. 나는 그것이 틀렸다고 말하지는 않지만 구현하려는 솔루션처럼 느끼지 않았습니다.

그래서 나에게 직관을 어 기지 않고 게시물 시작 부분에서 겪었던 문제를 해결하는 것은 여전히 ​​계층 구조를 사용하는 것이지만 이전에 사용했던 것과 같은 모 놀리 식 계층 구조는 아니지만 오히려 여러 개의 작은 나무로 구성된 다항식 (모 놀리 식과 반대되는 단어를 찾을 수 없음).

다음 예제는 내가 의미하는 바를 보여줍니다 (이는 14 장 Game Engine Architecture에서 찾은 예에서 영감을 얻었습니다).

나는 차량을위한 작은 나무를 가질 것입니다. 루트 비히클 클래스에는 렌더링 구성 요소, 충돌 구성 요소, 위치 구성 요소 등이 있습니다.

그런 다음 전차의 서브 클래스 인 전차는 전차에서 해당 구성 요소를 상속 받아 자체 "대포"구성 요소를받습니다.

캐릭터도 마찬가지입니다. 캐릭터는 자체 컴포넌트를 가지게되고, 플레이어 클래스는이를 상속 받아 입력 컨트롤러를 받게되고 다른 적 클래스는 캐릭터 클래스에서 상속 받아 AI 컨트롤러를받습니다.

이 디자인에는 아무런 문제가 없습니다. 순수한 엔터티 컨트롤러 시스템을 사용하지 않더라도 버블 링 효과와 큰 루트 클래스의 문제는 다중 트리 계층 구조를 사용하여 해결되며, 리프가 없어서 무거운 코드 복제 리프의 문제가 사라집니다. 구성 요소만으로 시작하는 코드가 있습니다. 리프 수준으로 변경해야하는 경우 코드를 모든 곳에 붙여 넣는 대신 단일 구성 요소를 변경하는 것만 큼 간단합니다.

물론, 나는 경험이 없어서 단일 계층 구조, 상속 무거운 모델을 처음 사용했을 때 아무런 문제도 보지 못했습니다. 따라서 현재 구현하려는 모델에 문제가 있으면 구현하지 않습니다. 그것을 볼 수 있습니다.

당신의 의견?

추신 : Java를 사용하고 있으므로 일반적인 구성 요소를 사용하는 대신 다중 상속을 사용하여이를 구현할 수 없습니다.

PPS : 구성 요소 간 통신은 종속 구성 요소를 서로 연결하여 수행됩니다. 이것은 커플 링으로 이어질 것이지만, 나는 그것이 괜찮은 거래라고 생각합니다.


2
이게 다 괜찮아 보입니다. 계층 또는 엔터티 시스템에 "냄새가 나는"특정 예가 있습니까? 결국, 게임을 순도 이상의 의미로 만드는 것이 전부입니다.
drhayes

나는 말하지 않았지만 거의 경험이 없으며 나는 이것을 위해 최고의 판사와는 거리가 멀다.
Midori Ryuu

3
나는이 질문이 주관적이고 광범위한 토론에 개방되어 있기 때문에이 질문을 마무리하기로 투표했습니다. 성실한 입장에서 요청 해 주셔서 감사합니다. 그러나 여기서 진행하는 방법을 선택하는 것은 전적으로 개인적인 의견의 문제입니다. 컴포넌트를 서브 클래스에 고정시키는 유일한 단점은 런타임에 컴포넌트의 배열을 변경할 수 없다는 점입니다. 그러나 반드시 사용자에게 분명해야하며 선택입니다.
Kylotan

4
나도 투표하기로했다. 훌륭하고 잘 작성된 질문이지만 GDSE에는 적합하지 않습니다. GameDev.net에서 이것을 다시 게시 할 수 있습니다.
Sean Middleditch

서면으로, '당신의 의견?'이라는 질문으로, 실제로 개방적이고 답할 수 없습니다. 그러나, 그것은 이다 당신이 질문 인 경우로 대답하면 답할 '어떤 입을 딱 벌리고있는 함정은 내가 모르게 빠질 수 있습니까?' (적어도, 실제로 다양한 아키텍처간에 경험적 차이가 있다고 생각되면)
John Calsbeek

답변:


8

이 예제를 고려하십시오.

당신은 RTS를 만들고 있습니다. 완벽한 논리의 적합, 당신은 기본 클래스를 만들하기로 결정 GameObject하고 두 개의 서브 클래스를, Building하고 Unit. 물론 이것은 잘 작동하며 다음과 같은 결과가 나타납니다.

  • GameObject
    • ModelComponent
    • CollisionComponent
  • Building
    • ProductionComponent
  • Unit
    • AimingAIComponent
    • WeaponComponent
    • ParticleEmitterComponent
    • AnimationComponent

이제 모든 하위 클래스에 Unit필요한 모든 구성 요소가 이미 있습니다. 그리고 보너스로, 당신은 모든 지루한 설치 코드를 넣어 한 곳이 new을들 WeaponComponent,에 연결 AimingAIComponent하고 ParticleEmitterComponent-wonderful!

물론 로직을 구성 요소에 추가하기 때문에 결국에는 Tower 건물이지만 무기가있는 클래스 하면 관리 할 수 ​​있습니다. 하위 클래스에 AimingAIComponent, a WeaponComponent및 a ParticleEmitterComponent를 추가 할 수 있습니다 Building. 물론 Unit클래스 에서 초기화 코드를 찾아서 다른 곳에 두어야하지만 큰 손실은 아닙니다.

그리고 지금, 당신이 가진 얼마나 많은 예언에 따라, 당신은 미묘한 버그로 끝날 수 있습니다. 게임의 다른 곳에 다음과 같은 코드가있을 수 있습니다.

if (myGameObject instanceof Unit)
    doSomething(((Unit)myGameObject).getWeaponComponent());

Tower비록 당신이 정말로 무기를 가진 어떤 것에도 효과가 있기를 원하더라도, 그것은 당신 의 건물에는 자동으로 작동하지 않습니다 . 이제 다음과 같이 보여야합니다.

WeaponComponent weapon = myGameObject.getComponent(WeaponComponent.class);
if (weapon)
    doSomething(weapon);

훨씬 낫다! 그것은 것을이 변경 후 발생 어떤 이 상황에서 끝낼 수 작성한 접근 방법. 가장 안전한 방법은 모든 것을 제거하고 모든 것에 액세스 하는 것입니다. 하고이 하나의 getComponent방법을 통해 구성 요소에 이 방법은 모든 하위 클래스에서 작동 할 수 있습니다. (또는 모든 단일 종류를 get*Component()수퍼 클래스에 추가 GameObject하고 하위 클래스에서 재정 의하여 구성 요소를 반환 할 수 있습니다. 첫 번째 것으로 가정합니다.)

지금 당신 BuildingUnit클래스는 접근자가 없어도 구성 요소가 많은 가방입니다. 또한 특별한 초기화는 필요하지 않습니다. 대부분의 특수 객체로 어딘가에 코드 중복을 피하기 위해 대부분을 드래그해야했기 때문입니다.

이것을 극단적으로 따르는 경우, 항상 서브 클래스가 완전히 비활성 인 컴포넌트 백을 만들게되는데,이 시점에서 서브 클래스가있는 지점조차 없습니다. 적절한 구성 요소를 생성 하는 메서드 계층 구조로 클래스 계층 구조를 사용하면 거의 모든 이점을 얻을 수 있습니다 . Unit클래스 를 사용하는 대신 , a 및 a를 호출 하는 , a 및 a addUnitComponents를 만드는 메소드 가 있습니다. 결과적으로 엔터티 시스템의 모든 이점, 계층 구조의 모든 코드 재사용 및 클래스 유형 을 확인 하거나 캐스트 하려는 유혹이 없습니다 .AnimationComponentaddWeaponComponentsAimingAIComponentWeaponComponentParticleEmitterComponentinstanceof


잘했다. 이것에 덧붙여 이상적인 해결책은 스크립트 또는 설명자 파일을 사용하여 각 엔티티를 초기화하는 것입니다. 엔터티 초기화에 텍스트 파일을 사용합니다. 빠르고 프로그래머가 아닌 사용자가 다른 매개 변수를 테스트 할 수 있습니다.
코요테

와우 당신의 게시물을 읽고 나에게 악몽을 주었다! 물론, 나는 당신이 내가 어떤 악몽을 겪었는지 눈을 뜨는 것을 보면서 좋은 방법으로 의미합니다! 자세한 답변에 더 답변 할 수 있기를 바랍니다.하지만 실제로 추가 할 것이 많지 않습니다!
Midori Ryuu

더 간단한 방법으로. 빈약 한 사람 팩토리 패턴으로 상속을 사용하지 않습니다.
Zardoz89

2

상속 에 대한 구성 은 OOP 용어 (적어도 내가 배우거나 가르친 방식)입니다. 구성과 상속 중에서 선택하려면 "Car is a wheel of wheel"(상속) 및 "Car has wheels"(구성)를 크게 말합니다. 하나는 다른 것보다이 소리가 더 정확해야하며 (이 경우 컴포지션) 결정할 수없는 경우 달리 결정할 때까지 기본적으로 컴포지션을 사용합니다.


1

게임 객체를 기반으로 기존 게임에 컴포넌트 기반 시스템을 통합하는 경우 카우보이 프로그래밍에 대한 기사가 있습니다 .http : //cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ 가 있습니다. 전환이 이루어질 수있는 방법.

문제와 관련하여 모델은 플레이어를 다른 적과 다르게 처리해야합니다. 플레이어가 연결을 끊거나 특수한 경우 다른 캐릭터와 다르게 처리하지 않으면 상태 (예 : 플레이어가 AI 제어 됨)를 전환 할 수 없습니다.

다른 엔티티에서 컴포넌트를 많이 재사용하는 것 외에도 컴포넌트 기반 게임이라는 아이디어는 전체 시스템의 균일 성입니다. 각 엔터티에는 마음대로 연결 및 분리 할 수있는 구성 요소가 있습니다. 동일한 종류의 모든 구성 요소는 각 종류의 인터페이스 (기본 구성 요소)에 의해 설정된 일련의 호출 (위치, 렌더링, 스크립트 ...)로 정확히 동일한 방식으로 처리됩니다.

게임 오브젝트 상속을 컴포넌트 기반 접근 방식과 혼합하면 두 가지 접근 방식의 단점이있는 컴포넌트 기반 접근 방식의 장점이 있습니다.

그것은 당신을 위해 일할 수 있습니다. 그러나 하나의 아키텍처에서 다른 아키텍처로의 전환 기간 외부에서 두 가지를 혼합하지 않았습니다.

PS는 전화로 작성되었으므로 외부 리소스에 연결하기가 어렵습니다.


전화를 걸어도 답장을 보내 주셔서 감사합니다! 무슨 뜻인지 이해합니다 구성 요소를 기반으로하는 이동은 변경해야 할 유연성이 높아집니다. 내가 최소한의 상속으로 컴포지션 접근법을 시도하는 이유입니다. (내가 제안한 것 이상)
Midori Ryuu

@MidoriRyuu는 엔티티 객체의 모든 상속을 피하십시오. 리플렉션 (및 내성)이 내장 된 언어를 사용하기에 운이 좋을 것입니다. 파일 (txt 또는 XML)을 사용하여 올바른 매개 변수로 객체를 초기화 할 수 있습니다. 너무 많은 작업이 아니며 재 컴파일하지 않고도 게임 미세 조정을 향상시킬 수 있습니다. 그러나 최소한 컴포넌트 간 통신 및 기타 유틸리티 작업을 위해서는 Entity 클래스를 유지하십시오. PS는 여전히 전화 :(
코요테
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.