개체 구성 요소 시스템이 디커플링 / 정보 숨기기에 끔찍하지 않습니까?


11

제목은 의도적으로 쌍곡 적이며 패턴에 대한 나의 경험이 아닐 수 있지만 여기에 내 추론이 있습니다.

엔터티를 구현하는 "일반적인"또는 논란의 여지가없는 방법은 그것들을 객체로 구현하고 일반적인 행동을 서브 클래 싱하는 것입니다. 의 고전적인 문제에 대한이 리드 "는 것입니다 EvilTree의 서브 클래스 TreeEnemy?". 다중 상속을 허용하면 다이아몬드 문제가 발생합니다. 우리는 대신 결합의 기능 당길 수 TreeEnemy하나님 클래스에있는 리드 계층까지 더를, 또는 우리는 의도적으로 우리의 행동을 남길 수 있습니다 TreeEntity(가) 너무 (그들이 극단적 인 경우에 인터페이스 만들기) 클래스 EvilTree에있는 리드를 - 그 자체를 구현할 수 있습니다 우리가 가진 코드 중복 SomewhatEvilTree.

엔티티 컴포넌트 시스템은 분할하여이 문제를 해결하려고 Tree하고 Enemy다른 구성 요소에 객체를 - 말 Position, Health그리고 AI같은 같은 시스템 및 구현 - AISystemAI 결정에 따라 실재물의 위치를 변경 그. 지금까지는 좋지만 EvilTree파워 업을 통해 피해를 입을 수 있다면 어떨까요? 먼저 a CollisionSystem와 a 가 필요합니다 DamageSystem. 다음 CollisionSystem과 통신해야합니다 DamageSystem. 두 가지가 충돌 할 때마다 CollisionSystem메시지를 보내기 DamageSystem때문에 상태를 뺄 수 있습니다. 파워 업에도 영향을 미치기 때문에 어딘가에 보관해야합니다. PowerupComponent엔티티에 첨부 할 새 를 작성 합니까? 그러나 그때DamageSystem아무 것도 모르는 것에 대해 알아야합니다. 결국 파워 업을받을 수없는 피해를 입히는 것들도 있습니다 (예 :) Spike. 우리는 허용 함 PowerupSystem수정 StatComponent도 유사 피해 계산에 사용되는 이 답변을 ? 그러나 이제 두 시스템이 동일한 데이터에 액세스합니다. 게임이 복잡 해짐에 따라 여러 시스템에서 구성 요소를 공유하는 무형의 의존성 그래프가됩니다. 그 시점에서 전역 정적 변수를 사용하고 모든 상용구를 제거 할 수 있습니다.

이것을 해결하는 효과적인 방법이 있습니까? 내가 가진 아이디어 중 하나는 구성 요소가 특정 기능을 갖도록하는 것입니다. 예를 들어 StatComponent attack()기본적으로 정수를 반환하지만 파워 업이 발생할 때 구성 할 수 있습니다.

attack = getAttack compose powerupBy(20) compose powerdownBy(40)

이것은 attack여러 시스템에서 액세스하는 구성 요소에 저장 해야하는 문제를 해결하지 못하지만 적어도 충분히 지원하는 언어가 있으면 함수를 올바르게 입력 할 수 있습니다.

// In StatComponent
type Strength = PrePowerup | PostPowerup
type Damage = Int
type PrePowerup = Int
type PostPowerup = Int
attack: Strength = getAttack //default value, can be changed by systems
getAttack: PrePowerup

// these functions can be defined in other components or in PowerupSystems
powerupBy: Strength -> PostPowerup
powerdownBy: Strength -> PostPowerup
subtractArmor: Strength -> Damage

// in DamageSystem
dealDamage: Damage -> () = attack compose subtractArmor compose hurtSomeEntity

이렇게하면 시스템에 의해 추가 된 다양한 기능의 올바른 순서를 보장합니다. 어느 쪽이든, 나는 여기서 기능적 반응성 프로그래밍에 빠르게 접근하고있는 것 같습니다. 처음부터 그것을 사용해서는 안되는지 나 자신에게 묻습니다 (FRP 만 보았으므로 여기에 잘못되었을 수 있습니다). ECS는 복잡한 클래스 계층 구조에 비해 개선 된 것이지만 그것이 이상적이라고 확신하지는 않습니다.

이 문제에 대한 해결책이 있습니까? ECS를보다 명확하게 분리하기 위해 누락 된 기능 / 패턴이 있습니까? FRP가이 문제에 더 적합합니까? 이 문제들이 내가 프로그램하려고하는 것의 본질적인 복잡성으로 인해 발생 하는가? 즉, FRP에도 비슷한 문제가 있습니까?



나는 Eric의 블로그를 그리워합니다 (C #에 관한 것부터).
OldFart

답변:


21

ECS는 데이터 숨기기를 완전히 망칩니다. 이것은 패턴의 절충입니다.

ECS는 디커플링 이 우수 합니다. 올바른 ECS를 통해 이동 시스템은 존재하는 엔티티 유형 또는 다른 시스템이 이러한 컴포넌트에 액세스하지 않아도 속도 및 위치 컴포넌트가있는 엔티티에서 작동한다고 선언 할 수 있습니다. 이것은 게임 오브젝트가 특정 인터페이스를 구현하도록하는 힘을 분리하는 데있어 적어도 동일합니다.

동일한 구성 요소에 액세스하는 두 시스템은 문제가 아닌 기능입니다. 그것은 완전히 기대되며 어떤 식 으로든 시스템을 결합하지 않습니다. 시스템에 암시 적 종속성 그래프가있는 것은 사실이지만 이러한 종속성은 모델링 된 세계에 내재되어 있습니다. 손상 시스템이 파워 업 시스템에 대한 암시 적 의존성을 가져서는 안된다는 것은 파워 업이 손상에 영향을 미치지 않는다고 주장하는 것입니다. 그러나 종속성이 존재하는 동안 시스템은 연결 되어 있지 않습니다 . 통신은 통계 구성 요소를 통해 발생하고 완전히 암시 적이기 때문에 피해 시스템에 영향을주지 않고 게임에서 파워 업 시스템을 제거 할 수 있습니다.

DI 시스템의 종속성 해결 방식과 유사하게 단일 종속성 위치에서 이러한 종속성 및 주문 시스템을 해결할 수 있습니다. 그렇습니다. 복잡한 게임에는 복잡한 시스템 그래프가 있지만이 복잡성은 고유하며 최소한 포함되어 있습니다.


7

시스템이 여러 구성 요소에 액세스해야한다는 사실을 해결할 방법이 거의 없습니다. VelocitySystem과 같은 것이 작동하려면 VelocityComponent 및 PositionComponent에 액세스해야합니다. 한편 RenderingSystem도이 데이터에 액세스해야합니다. 무엇을하든 렌더링 시스템은 어느 시점에서 오브젝트를 렌더링 할 위치를 알아야하고 VelocitySystem은 오브젝트를 어디로 이동 할지를 알아야합니다.

이것에 필요한 것은 종속성 의 명시 성 입니다. 각 시스템 은 어떤 데이터를 읽고 어떤 데이터를 쓸 것인지 명시 해야 합니다. 시스템이 특정 구성 요소를 가져 오려면이를 명시 적으로 만 수행 할 수 있어야 합니다 . 가장 간단한 형식으로, 필요한 각 유형의 컴포넌트 (예 : RenderSystem에는 RenderComponents 및 PositionComponents가 필요함)를 인수로 사용하고 변경된 내용을 리턴합니다 (예 : RenderComponents 만).

이렇게하면 시스템에 의해 추가 된 다양한 기능의 올바른 순서를 적어도 보장합니다.

그런 디자인으로 주문할 수 있습니다. ECS의 경우 시스템이 순서 나 그와 무관해야한다는 말은 없습니다.

FRP가이 문제에 더 적합합니까? 이 문제들이 내가 프로그램하려고하는 것의 본질적인 복잡성으로 인해 발생 하는가? 즉, FRP에도 비슷한 문제가 있습니까?

이 엔터티 구성 요소 시스템 디자인과 FRP를 사용하는 것은 상호 배타적이지 않습니다. 실제로 시스템은 상태가없는 것으로 간주 할 수 있으며 단순히 데이터 변환 (구성 요소)을 수행합니다.

FRP는 일부 작업을 수행하기 위해 필요한 정보를 사용해야하는 문제를 해결하지 못합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.