В комментариях я разместил сокращенную версию моего ответа старого ответа:
Использование DrawableGameComponent
фиксирует вас в едином наборе сигнатур методов и конкретной сохраненной модели данных. Обычно это изначально неправильный выбор, и блокировка значительно препятствует развитию кода в будущем.
Здесь я попытаюсь проиллюстрировать, почему это так, разместив некоторый код из моего текущего проекта.
Корневой объект, поддерживающий состояние игрового мира, имеет Update
метод, который выглядит следующим образом:
void Update(UpdateContext updateContext, MultiInputState currentInput)
Существует несколько точек входа в Update
зависимости от того, запущена игра в сети или нет. Например, возможно, чтобы игровое состояние было возвращено к предыдущему моменту времени, а затем повторно имитировано.
Есть также некоторые дополнительные методы, подобные обновлениям, такие как:
void PlayerJoin(int playerIndex, string playerName, UpdateContext updateContext)
В игровом состоянии есть список актеров, которые будут обновлены. Но это больше, чем простой Update
звонок. Есть дополнительные проходы до и после, чтобы справиться с физикой. Есть также некоторые интересные вещи для отложенного появления / уничтожения актеров, а также переходы уровней.
За пределами игрового состояния существует система пользовательского интерфейса, которой необходимо управлять независимо. Но некоторые экраны в пользовательском интерфейсе также должны работать в сетевом контексте.
Для рисования, из-за характера игры, существует специальная система сортировки и отбора, которая принимает данные от следующих методов:
virtual void RegisterToDraw(SortedDrawList sortedDrawList, Definitions definitions)
virtual void RegisterShadow(ShadowCasterList shadowCasterList, Definitions definitions)
И тогда, когда он выясняет, что рисовать, автоматически вызывает:
virtual void Draw(DrawContext drawContext, int tag)
tag
Параметр необходим , поскольку некоторые участники могут держать на другие актер - и поэтому должны быть в состоянии сделать как выше , так и ниже задержанный актер. Система сортировки гарантирует, что это происходит в правильном порядке.
Существует также система меню для рисования. И иерархическая система для рисования пользовательского интерфейса, вокруг HUD, вокруг игры.
Теперь сравните эти требования с тем, что 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
и не хотят признавать себя «неправильными». Таким образом, они фиксируют тот факт, что, по крайней мере, можно заставить его «работать».
Конечно, это можно сделать, чтобы «работать»! Мы программисты - заставить вещи работать под необычными ограничениями - это практически наша работа. Но эта сдержанность не нужна, полезна или рациональна ...
Что особенно flabbergasting о том , что это поразительно тривиальной боковой шаг весь этот вопрос. Здесь я даже дам вам код:
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
из моего кода. Или еще хуже: взломать его каким-либо образом, что сделает его еще сложнее, когда в следующий раз возникнет такая необходимость.
На самом деле есть только один сценарий, в котором это хороший выбор DrawableGameComponent
: если вы буквально создаете и публикуете промежуточное программное обеспечение в стиле перетаскивания для XNA. Что было его первоначальной целью. Который - добавлю - никто не делает!
(Точно так же это имеет смысл использоватьServices
при создании пользовательских типов контента для ContentManager
.)