엔티티와 컴포넌트에 메소드를 저장하는 것은 왜 나쁜 생각입니까? (다른 엔티티 시스템 질문과 함께.)


16

이것은 후속 조치입니다 내가 대답 한이 질문에 이지만 훨씬 구체적인 주제를 다루고 있습니다.

이 답변을 통해 기사보다 엔티티 시스템을 더 잘 이해할 수있었습니다.

엔터티 시스템에 대한 기사를 읽었 으며 다음과 같이 말했습니다.

엔터티 는 ID와 구성 요소의 배열 일뿐입니다 (이 기사에서는 구성 요소에 엔터티를 저장하는 것이 좋은 방법은 아니지만 대안을 제공하지는 않음).
구성 요소 는 특정 엔터티로 수행 할 수있는 작업을 나타내는 데이터 조각입니다.
시스템 은 "메소드"이며 엔터티에 대한 데이터 조작을 수행합니다.

이것은 많은 상황에서 실제로 실용적으로 보이지만 데이터 클래스 인 구성 요소에 대한 부분은 나를 귀찮게합니다. 예를 들어 엔터티 시스템에서 Vector2D 클래스 (위치)를 어떻게 구현할 수 있습니까?

Vector2D 클래스는 x, y 좌표와 같은 데이터를 보유하지만 , 유용성에있어 결정적인 두 개의 요소 배열과 클래스를 구분하는 methods 도 있습니다 . 예제 방법은 다음과 같습니다 add(). rotate(point, r, angle),,substract() , normalize(), 및 기타 모든 기준은, 유용하고, 절대적으로 필요한 방법합니다 (Vector2D 클래스의 인스턴스) 위치는 있어야한다고.

구성 요소가 데이터 홀더 인 경우 이러한 방법을 사용할 수 없습니다!

아마도 팝업 될 수있는 한 가지 해결책은 시스템 내부에서 구현하는 것이지만 매우 직관적입니다. 이 방법들은 내가 지금 수행하고 싶은 것들이며 , 완성되어 사용할 준비가 된 것입니다. MovementSystem엔터티 위치에 대한 계산을 수행하도록 지시하는 값 비싼 메시지 세트를 읽을 때까지 기다리고 싶지 않습니다 !

그리고, 기사는 매우 명확하게한다고 시스템이 있어야 하나를 "피하기 OOP에"있었다, 기능, 그리고 내가 찾을 수있는 유일한 설명. 우선 엔티티와 컴포넌트에서 메소드를 사용하지 않는 이유를 이해하지 못합니다. 메모리 오버 헤드는 실질적으로 동일하며 시스템과 결합 될 때 흥미로운 방식으로 구현하고 결합하기가 매우 쉬워야합니다. 예를 들어, 시스템은 구현 자체를 알고있는 엔티티 / 구성 요소에 기본 로직 만 제공 할 수 있습니다. 당신이 나에게 묻는다면-이것은 기본적으로 ES와 OOP 모두에서 좋은 점을 취한 것입니다.이 기사의 저자에 따르면 할 수 없지만 좋은 습관처럼 보입니다.

이런 식으로 생각하십시오. 게임에는 다양한 유형의 드로어 블 객체가 있습니다. 평범한 오래된 이미지, 애니메이션 ( update(),getCurrentFrame() 등), 이러한 기본 유형의 조합 및 그 모두는 단순히 draw()렌더링 시스템에 메소드를 제공 할 수 있으며 엔티티의 스프라이트가 어떻게 구현되는지 신경 쓰지 않아도됩니다. 인터페이스 (드로우)와 위치에 대해 그런 다음 렌더링과 관련이없는 애니메이션 관련 메서드를 호출하는 애니메이션 시스템 만 있으면됩니다.

그리고 한 가지 더 ... 구성 요소를 저장할 때 배열에 대한 대안이 실제로 있습니까? Entity 클래스 내부의 배열 이외의 구성 요소를 저장할 수있는 다른 곳은 없습니다 ...

어쩌면 이것은 더 나은 접근 방법입니다. 구성 요소를 엔티티의 간단한 속성으로 저장하십시오. 예를 들어 위치 구성 요소는entity.position .

유일한 다른 방법은 이상한 조회 테이블의 일종해야하는 것입니다 내부 , 시스템을 참조 다른 실체가. 그러나 단순히 엔티티에 구성 요소를 저장하는 것보다 개발이 매우 비효율적이고 복잡해 보입니다 .


Alexandre 당신은 다른 배지를 얻기 위해 많은 편집을하고 있습니까? 그것은 장난 꾸러기이기 때문에, 수많은 고대 실이 계속 부딪칩니다.
jhocking 2016 년

답변:


25

구성 요소의 데이터에 액세스, 업데이트 또는 조작하는 간단한 방법을 사용하는 것이 좋습니다. 구성 요소에서 벗어나야하는 기능은 논리적 기능이라고 생각합니다. 유틸리티 기능은 괜찮습니다. 엔터티 구성 요소 시스템은 지침 일 뿐이며 따라야 할 엄격한 규칙은 아닙니다. 그들을 따라가는 길을 떠나지 마십시오. 한 가지 방법으로하는 것이 더 합리적이라고 생각되면 그렇게하십시오 :)

편집하다

명확히하기 위해, 당신은 OOP 를 피하지 않는 것이 목표입니다 . 요즘 사용되는 대부분의 공용 언어에서는 꽤 어려울 것입니다. 상속 을 최소화하려고합니다. 합니다. OOP의 큰 측면이지만 필수는 아닙니다. Object-> MobileObject-> Creature-> Bipedal-> Human type 상속을 제거하려고합니다.

그러나 상속을받는 것은 괜찮습니다! 상속에 크게 영향을받는 언어를 다루고 있는데, 언어를 사용하지 않는 것은 매우 어렵습니다. 예를 들어 Component다른 모든 구성 요소가 확장하거나 구현 하는 클래스 또는 인터페이스를 가질 수 있습니다 . 귀하와 같은 거래 System클래스입니다. 이것은 일을 훨씬 쉽게 만듭니다. 아르테미스를 살펴 보는 것이 좋습니다. 프레임 워크를 . 오픈 소스이며 예제 프로젝트가 있습니다. 그 것들을 열고 그것이 어떻게 작동하는지보십시오.

Artemis의 경우 엔터티는 간단한 배열로 저장됩니다. 그러나 해당 구성 요소는 배열 (엔터티와 분리)에 저장됩니다. 최상위 수준 배열은 하위 수준 배열을 구성 요소 유형별로 그룹화합니다. 따라서 각 구성 요소 유형에는 고유 한 배열이 있습니다. 하위 레벨 배열은 엔티티 ID별로 색인화됩니다. (이제 그렇게 할 지 확신 할 수 없지만 그것이 여기서 수행되는 방식입니다). Artemis는 엔터티 ID를 재사용하므로 최대 엔터티 ID는 현재 엔터티 수보다 크지 않지만 구성 요소가 자주 사용되는 구성 요소가 아닌 경우에도 희소 배열을 가질 수 있습니다. 어쨌든, 나는 그렇게 많이 따지 않을 것입니다. 엔티티 및 해당 컴포넌트를 저장하는이 방법이 작동하는 것 같습니다. 자신의 시스템을 구현하는 데 큰 도움이 될 것이라고 생각합니다.

엔터티와 구성 요소는 별도의 관리자에 저장됩니다.

엔티티가 자체 컴포넌트 ( entity.position)를 저장하도록하는 전략 은 엔티티 컴포넌트 테마와는 다소 상충되지만, 가장 적절하다고 생각되면 완전히 허용됩니다.


1
흠, 그것은 상황을 크게 단순화시킵니다. 감사합니다! 나는 "나중에 후회할 것"이라는 마법이 있다고 생각했는데, 그것을 볼 수 없었습니다!
jcora

1
나는 엔터티 구성 요소 시스템에서 완전히 사용합니다. 심지어 공통 부모 인 gasp 에서 상속받은 일부 구성 요소가 있습니다. 나는 당신이 그렇게 유감스럽게 생각하는 것은 그런 방법을 사용 하지 않고 해결하려고 노력하는 것입니다. 당신에게 가장 적합한 것을하는 것이 전부입니다. 상속을 사용하거나 구성 요소에 일부 메소드를 넣는 것이 합리적이라면 그것을 선택하십시오.
MichaelHouse

2
이 주제에 대한 마지막 답변에서 배웠습니다. 면책 조항 : 나는 이것이 그것을 하는 방법 이라고 말하지 않습니다 . :)
MichaelHouse

1
네, 새로운 패러다임을 배우는 것이 얼마나 어려운지 압니다. 운 좋게도 이전 패러다임의 측면을 사용하여 일을 더 쉽게 만들 수 있습니다! 스토리지 정보로 답변을 업데이트했습니다. 아르테미스를 보면 EntityManager물건이 저장된 곳을 확인하십시오 .
MichaelHouse

1
좋은! 그것이 끝나면 꽤 좋은 엔진이 될 것입니다. 행운을 빕니다! 흥미로운 질문을 해주셔서 감사합니다.
MichaelHouse

10

'그 기사'는 내가 특히 동의하는 것이 아니기 때문에 제 대답은 다소 비판적이라고 생각합니다.

이것은 많은 상황에서 실제로 실용적으로 보이지만 데이터 클래스 인 구성 요소에 대한 부분은 나를 귀찮게합니다. 예를 들어 엔터티 시스템에서 Vector2D 클래스 (위치)를 어떻게 구현할 수 있습니까?

아이디어는 프로그램의 어떤 것도 엔티티 ID, 구성 요소 또는 시스템 이외의 것을 보장하지 않습니다. 복잡한 상속 트리를 사용하거나 더 나쁜 시도를하지 않고 객체를 구성하여 엔티티 데이터와 동작을 생성해야합니다. 가능한 모든 기능을 하나의 객체에 넣습니다. 이러한 구성 요소와 시스템을 구현하려면 대부분의 언어에서 클래스로 가장 잘 표현되는 벡터와 같은 일반 데이터가 있어야합니다.

이 기사에서는 이것이 OOP가 아니라고 제안하는 비트를 무시하십시오. 다른 접근 방식과 마찬가지로 OOP입니다. 대부분의 컴파일러 또는 언어 런타임이 객체 메소드를 구현할 때 기본적으로 다른 인수와 비슷하지만 숨겨진 인수가 있습니다.this 또는 self은 객체의 데이터가 저장된 메모리의 위치에 대한 포인터 인 가 있습니다. 컴포넌트 기반 시스템에서 엔티티 ID는 주어진 엔티티의 관련 컴포넌트 (및 데이터)가있는 위치를 찾는 데 사용될 수 있습니다. 따라서 엔티티 ID는 this / self 포인터와 동일하며 개념은 기본적으로 동일하며 약간 재정렬됩니다.

그리고이 기사는 시스템에만 기능이 있어야한다고 명시하고 있으며, 이것에 대한 유일한 설명은 "OOP를 피하는 것"이었습니다. 우선 엔티티와 컴포넌트에서 메소드를 사용하지 않는 이유를 이해하지 못합니다.

좋은. 메소드는 코드를 효과적으로 구성하는 방법입니다. "OOP 피하기"아이디어에서 벗어나야 할 중요한 점은 기능을 확장하기 위해 어디서나 상속을 사용하지 않는 것입니다. 대신 기능을 결합하여 동일한 작업을 수행 할 수있는 구성 요소로 나눕니다.

이런 식으로 생각하십시오. 게임에는 다양한 유형의 드로어 블 객체가 있습니다. 평범한 오래된 이미지, 애니메이션 (update (), getCurrentFrame () 등), 이러한 기본 유형의 조합 및 모든 렌더링 시스템에 draw () 메소드를 제공 할 수 있습니다 [...]

구성 요소 기반 시스템의 아이디어는 별도의 클래스가 없지만 단일 객체 / 엔티티 클래스를 가지며 이미지는 ImageRenderer가있는 객체 / 엔티티이고 애니메이션은 객체 / AnimationRenderer 등이있는 엔터티

[...] 그러면 엔터티의 스프라이트가 어떻게 구현되는지, 인터페이스 (그리기) 및 위치 만 신경 쓰지 않아도됩니다. 그런 다음 렌더링과 관련이없는 애니메이션 관련 메서드를 호출하는 애니메이션 시스템 만 있으면됩니다.

물론 이것은 구성 요소와 잘 작동하지 않습니다. 3 가지 선택이 있습니다 :

  • 모든 구성 요소는이 인터페이스를 구현하며 아무 것도 그려지지 않더라도 Draw () 메서드를 갖습니다. 모든 기능에 대해이 작업을 수행하면 구성 요소가보기 흉하게 보입니다.
  • 그릴 무언가가있는 컴포넌트 만 인터페이스를 구현하지만 누가 Draw ()를 호출 할 컴포넌트를 결정합니까? 어떤 인터페이스가 지원되는지 확인하려면 시스템에서 각 구성 요소를 어떻게 쿼리해야합니까? 오류가 발생하기 쉽고 일부 언어로 구현하기가 까다로울 수 있습니다.
  • 구성 요소는 소유 시스템 (링크 된 기사의 아이디어)에 의해서만 처리됩니다. 어떤 경우에는 시스템이 작업하는 클래스 또는 객체 유형을 정확히 알고 있기 때문에 인터페이스가 중요하지 않습니다.

그리고 한 가지 더 ... 구성 요소를 저장할 때 배열에 대한 대안이 실제로 있습니까? Entity 클래스 내부의 배열 이외의 구성 요소를 저장할 수있는 다른 곳은 없습니다 ...

구성 요소를 시스템에 저장할 수 있습니다. 배열은 문제가 아니지만 구성 요소를 저장하는 위치입니다.


+1 다른 관점에 감사드립니다. 그런 모호한 주제를 다룰 때 몇 가지를 얻는 것이 좋습니다! 구성 요소를 시스템에 저장하는 경우 구성 요소를 한 시스템에서만 수정할 수 있습니까? 예를 들어, 드로잉 시스템과 이동 시스템은 모두 위치 구성 요소에 액세스합니다. 어디에 보관합니까?
MichaelHouse

글쎄, 그들은 어딘가에 관심이 있는 한 그 구성 요소에 대한 포인터 만 저장할 것입니다 ... 또한 구성 요소를 시스템에 저장하는 이유는 무엇입니까? 이점이 있습니까?
jcora

@Kylotan 맞습니까? 그것이 제가 그렇게하는 방법입니다. 논리적으로 보입니다.
jcora

Adam / T-Machine의 예에서, 구성 요소 당 하나의 시스템이 있지만 시스템은 다른 구성 요소에 확실히 액세스하고 수정할 수 있습니다. (이것은 컴포넌트의 멀티 스레딩 이점을 방해하지만 다른 문제입니다.)
Kylotan

1
시스템에 구성 요소를 저장하면 해당 시스템에 대한 참조의 지역성이 향상됩니다. 해당 시스템 만 해당 데이터와 만 작동하므로 컴퓨터 메모리 전체를 엔터티에서 엔터티로 가져 가서 가져 오는 이유는 무엇입니까? 또한 전체 시스템과 해당 데이터를 하나의 코어 나 프로세서 (또는 MMO의 별도 컴퓨터)에 둘 수 있기 때문에 동시성에 도움이됩니다. 다시 말하지만, 1 개의 시스템이 1 개 이상의 유형의 구성 요소에 액세스 할 때 이러한 이점이 줄어들므로 구성 요소 / 시스템 책임을 분할 할 위치를 결정할 때 고려해야합니다.
Kylotan

2

벡터는 데이터입니다. 함수는 유틸리티 함수와 비슷합니다. 데이터의 해당 인스턴스에 국한되지 않고 모든 벡터에 독립적으로 적용될 수 있습니다. 그것에 대해 좋은 생각은 다음과 같습니다. 이러한 함수를 정적 메소드로 다시 작성할 수 있습니까? 그렇다면 유틸리티 일뿐입니다.


나는 알고 있지만 문제는 호출 방법이 더 빠르며 시스템이나 엔티티의 위치를 ​​조작해야 할 수도있는 다른 방법 으로 그 자리에서 수행 할 수 있다는 것입니다. 나는 또한 그것을 확인하고, 또한 질문은 이것보다 훨씬 더 많은 것이라고 설명했다.
jcora
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.