의견에, 나는 이전 답변의 요약 버전을 게시했습니다.
를 사용하면 DrawableGameComponent
단일 메소드 서명 세트 및 특정 보유 데이터 모델에 고정됩니다. 이것들은 일반적으로 처음에는 잘못된 선택이며 잠금 기능은 코드의 진화를 크게 방해합니다.
여기에서는 현재 프로젝트에서 일부 코드를 게시하여 이것이 왜 그런지 설명하려고 시도합니다.
게임 세계의 상태를 유지하는 루트 객체에는 Update
다음과 같은 방법이 있습니다.
void Update(UpdateContext updateContext, MultiInputState currentInput)
Update
게임이 네트워크에서 실행 중인지 여부에 따라 여러 가지 진입 점이 있습니다. 예를 들어 게임 상태를 이전 시점으로 롤백 한 다음 다시 시뮬레이션 할 수 있습니다.
다음과 같은 추가 업데이트와 같은 방법도 있습니다.
void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)
게임 상태에는 업데이트 할 액터 목록이 있습니다. 그러나 이것은 단순한 Update
호출 이상 입니다. 물리를 다루기 전후에 추가적인 패스가 있습니다. 액터의 연기 스폰 / 파괴뿐만 아니라 레벨 전환에도 멋진 기능이 있습니다.
게임 상태 이외에는 독립적으로 관리해야하는 UI 시스템이 있습니다. 그러나 UI의 일부 화면은 네트워크 컨텍스트에서 실행될 수 있어야합니다.
게임의 특성상 그리기에는 다음과 같은 방법으로 입력을받는 사용자 정의 정렬 및 컬링 시스템이 있습니다.
virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)
그리고 나서 무엇을 그릴 지 알아 내면 자동으로 다음을 호출합니다.
virtual void Draw(DrawContext drawContext, int tag)
tag
매개 변수는 어떤 배우가 다른 배우에 저장할 수 있기 때문에 필요합니다 - 그리고 필요하므로 보류 배우 위와 아래 모두 그릴 수 있도록. 정렬 시스템은 이것이 올바른 순서로 이루어 지도록합니다.
그릴 메뉴 시스템도 있습니다. 그리고 HUD 주변, 게임 주변에 UI를 그리는 계층 적 시스템.
이제 이러한 요구 사항을 DrawableGameComponent
제공 하는 내용과 비교하십시오 .
public class DrawableGameComponent
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
public int UpdateOrder { get; set; }
public int DrawOrder { get; set; }
}
DrawableGameComponent
위에서 설명한 시스템을 사용하여 시스템을 얻는 합리적인 방법은 없습니다 . (그리고 그것들은 다소 복잡한 게임에 대한 특별한 요구 사항이 아닙니다).
또한 이러한 요구 사항은 프로젝트 시작시 단순히 시작되지 않았다는 점에 유의해야합니다. 그들은 수개월에 걸친 개발 작업을 통해 진화했습니다.
사람들이 일반적으로 DrawableGameComponent
경로를 시작하면 해킹을 시작합니다.
메소드를 추가하는 대신 자신의 기본 유형으로 추악한 다운 캐스팅을 수행합니다 (직접 사용하지 않는 이유는 무엇입니까?).
정렬 및 호출 Draw
/ 의 코드를 바꾸는 대신 Update
주문 번호를 즉시 변경하기 위해 매우 느린 작업을 수행합니다.
그리고 최악의 방법 : 메소드에 매개 변수를 추가하는 대신, 스택이 아닌 메모리 위치에서 "파라미터"를 전달하기 시작합니다 (이 용도로 Services
널리 사용됩니다). 이것은 복잡하고 오류가 발생하기 쉽고 느리고 깨지기 쉽고 스레드 안전성이 거의 없으며 네트워킹을 추가하거나 외부 도구를 만드는 등의 경우 문제가 될 수 있습니다.
또한 API 경계에 게시되는 대신 종속성이 "구성 요소"안에 숨겨지기 때문에 코드 재사용이 더 어려워 집니다.
또는 그들은 이 모든 것이 얼마나 미친 지 거의 알고 있으며 DrawableGameComponent
이러한 문제를 해결하기 위해 "관리자"를 시작 합니다. "관리자"경계에서 여전히 이러한 문제가 있습니다.
이러한 "관리자"는 원하는 순서대로 일련의 메소드 호출로 쉽게 대체 할 수 있습니다. 다른 것은 너무 복잡합니다.
물론 일단 해킹을 사용하기 시작 하면 제공하는 것보다 더 많은 것이 필요하다는 것을 알게되면 해킹 을 취소 하기 위해 실제 작업 이 필요 DrawableGameComponent
합니다. 당신은 " 잠겨 있습니다 ". 그리고 유혹은 종종 해킹에 계속 쌓이는 경향이 있습니다. 실제로 해킹에 빠지고 상대적으로 단순한 게임으로 만들 수있는 게임의 종류를 제한합니다.
많은 사람들이이 시점에 도달하고 내장 반응을 보이는 것 같습니다. 아마도 그들이 DrawableGameComponent
"잘못된"것을 받아들이고 싶지 않기 때문일 것 입니다. 그래서 그들은 적어도 그것이 "작동"하게하는 것이 가능 하다는 사실을 고수합니다 .
물론 이 될 수있다 만든 "작업"에! 우리는 프로그래머입니다. 비정상적인 구속 조건에서 일을하는 것은 실질적으로 우리의 일입니다. 그러나이 구속은 필요하지 않거나 유용하거나 합리적이지 않습니다 ...
특히 불쾌감 을주는 것은 이 전체 문제를 회피 하는 것이 놀랍도록 사소한 것입니다. 여기, 나는 당신에게 코드를 줄 것이다 :
public class Actor
{
public virtual void Update(GameTime gameTime);
public virtual void Draw(GameTime gameTime);
}
List<Actor> actors = new List<Actor>();
foreach(var actor in actors)
actor.Update(gameTime);
foreach(var actor in actors)
actor.Draw(gameTime);
(그것의 단순에 따라 정렬에 추가 DrawOrder
하고 UpdateOrder
. 한 가지 간단한 방법은 두 개의 목록 및 사용을 유지하는 것입니다 List<T>.Sort()
. 그것은 또한 핸들에 간단하다 Load
/ UnloadContent
.)
그 코드가 실제로 무엇을 중요하지 않습니다 이다 . 중요한 것은 내가 사소하게 수정할 수 있다는 것 입니다.
에 다른 타이밍 시스템이 gameTime
필요하거나 더 많은 매개 변수 (또는 UpdateContext
약 15 가지 항목이 포함 된 전체 객체)가 필요하거나 드로잉을 위해 완전히 다른 작업 순서가 필요하거나 추가 방법이 필요하다는 것을 알 때 다른 업데이트 패스의 경우 계속 진행 하면 됩니다.
그리고 나는 그것을 즉시 할 수 있습니다. DrawableGameComponent
코드를 고르는 것에 대해 난처 할 필요가 없습니다 . 또는 더 나쁜 것은 다음과 같은 요구가 발생할 때 더 어려워 질 수있는 방식으로 해킹하는 것입니다.
XNA 용 드래그 앤 드롭 구성 요소 스타일 미들웨어를 문자 그대로 작성하고 게시하는 경우 사용하기에 가장 적합한 시나리오 는 실제로 하나 뿐입니다 . 그게 원래의 목적이었습니다. 어느 쪽도 추가 하지 않겠습니다.DrawableGameComponent
마찬가지로,에 대한 맞춤 콘텐츠 유형을 만들 때만 사용하는 것이 좋습니다Services
ContentManager
.