Трудная часть ответа на ваш вопрос заключается в том, что вы спрашиваете одновременно: «что мне делать сейчас для Pong» и «что мне делать позже в какой-то общей игре».
Чтобы играть в Pong, вам даже не нужны классы Ball и Paddle, потому что это просто позиции. Просто вставьте что-то вроде этого в свой класс Game:
Vector2 ballPosition, ballVelocity;
float leftPaddlePosition, rightPaddlePosition;
Затем просто обновите и нарисуйте их в любом удобном для вас порядке в функциях Update
и Draw
вашей игры. Легкий!
Но, скажем, вы хотите создать несколько мячей, а у мячей есть много свойств (положение, скорость, вращение, цвет и т. д.): вы можете создать класс или структуру Ball
, которую вы можете использовать (то же самое касается ракеток). Вы даже можете переместить некоторые функции в этот класс, где они будут автономными (хорошим примером является функция Draw
).
Но оставьте концепцию дизайна прежней — вся обработка взаимодействия объекта с объектом (т. е. игровой процесс) происходит в вашем классе Game
.
Все это прекрасно, если у вас есть два или три разных элемента геймплея (или класса).
Однако предположим более сложную игру. Давайте возьмем базовую игру в понг, добавим некоторые элементы пинбола, такие как мультибол и управляемые игроком ласты. Давайте добавим некоторые элементы из Змеи, скажем, у нас есть «змея», управляемая ИИ, а также некоторые объекты для захвата, в которые могут попасть либо шары, либо змея. И на всякий случай скажем, что весла также могут стрелять лазерами, как в Space Invaders, а лазерные стрелы делают разные вещи в зависимости от того, во что они попадают.
Боже мой, это огромный беспорядок взаимодействия! Как мы собираемся справиться с этим? Мы не можем поместить все это в игру!
Простой! Мы создаем интерфейс (или абстрактный класс, или виртуальный класс), из которого будет происходить каждая «вещь» (или «действующее лицо») в нашем игровом мире. Вот пример:
interface IActor
{
void LoadContent(ContentManager content);
void UnloadContent();
void Think(float seconds);
void UpdatePhysics(float seconds);
void Draw(SpriteBatch spriteBatch);
void Touched(IActor by);
Vector2 Position { get; }
Rectangle BoundingBox { get; }
}
(Это только пример. Не существует «одного истинного интерфейса актера», который будет работать для каждой игры, вам нужно будет создать свой собственный. Вот почему мне не нравится DrawableGameComponent
.)
Наличие общего интерфейса позволяет Game просто говорить об Актерах — вместо того, чтобы знать о каждом отдельном типе в вашей игре. Осталось сделать общие для всех типов вещи — обнаружение коллизий, отрисовка, обновление, загрузка, выгрузка и т. д.
Освоив в актера, вы можете начать беспокоиться об определенных типах актеров. Например, это может быть метод в Paddle
:
void Touched(IActor by)
{
if(by is Ball)
((Ball)by).BounceOff(this.BoundingBox);
if(by is Snake)
((Snake)by).Kill();
}
Теперь мне нравится делать мяч, отскакивающий от ракетки, но на самом деле это дело вкуса. Вы могли бы сделать это наоборот.
В конце вы сможете объединить всех своих актеров в большой список, который вы сможете просто просмотреть в игре.
На практике у вас может получиться несколько списков акторов разных типов из соображений производительности или простоты кода. Это нормально, но в целом старайтесь придерживаться принципа игры, зная только об общих актерах.
Актеры также могут захотеть узнать, какие другие акторы существуют по разным причинам. Так что дайте каждому актеру ссылку на Game и сделайте список актеров общедоступным в Game (нет необходимости быть слишком строгим в отношении общедоступного/частного, когда вы пишете код игрового процесса, и это ваш собственный внутренний код).
Теперь вы можете пойти еще дальше и иметь несколько интерфейсов. Например: один для рендеринга, один для сценариев и ИИ, один для физики и т. д. Затем создайте несколько реализаций, которые можно объединить в объекты.
Это подробно описано в этой статье. И у меня есть простой пример в этом ответе. Это подходящий следующий шаг, если вы начинаете обнаруживать, что ваш интерфейс с одним субъектом начинает превращаться в «дерево» абстрактных классов.
28.03.2011