Что не так с архитектурой рисования игрового объекта и само обновление?

В чем причины и против рисования и обновления игрового объекта? Например, если у вас есть игра, в которой игрок имеет позицию на экране, почему бы не включить всеохватывающий класс:

public class Player {
    private int x, y, xVelocity, yVelocity;
    private Sprite s;
    //...

    public Player() {
        // load the sprite here, somehow?
    }

    public void draw(CustomGraphicsClass g) {
        g.draw(s, x, y);
    }

    public void update(long timeElapsed) {
        x += (xVelocity * timeElapsed);
        y += (yVelocity * timeElapsed);
    }
}

Что не так с этим дизайном? Каковы проблемы или проблемы? Как бы вы лучше пишете что-то вроде этого или лучше архитектор такого типа в игре?

Также, несколько связанный, как бы вы реализуете загрузку этого изображения Sprite?

И, кроме того, как вы могли бы выполнить столкновение между двумя Player s?

(Вероятно, я должен отделить эти дополнительные два вопроса на новые вопросы, да?)

Ответы

Ответ 1

Он объединяет весь ваш код рендеринга и логики, когда у них мало общего, кроме того, что он концептуально связан с одним и тем же "сущностью". По мере того как ваш класс становится больше, вы можете оказаться с огромным монолитным Player, чтобы кошмар поддерживать. Разделение вдоль границ домена (рендеринг, AI) делает его более управляемым без необходимости сдаваться, поскольку эти домены не имеют большого перекрытия.

Помимо ремонтопригодности, есть и другие соображения:

  • Если вы хотите обработать рендеринг и ИИ на разных потоках, смешение их состояния в одном классе - просто попрошайничество против неприятных проблем с несколькими потоками.

  • Если вы используете такой язык, как С++, такие высокоуровневые классы, как это, могут убить время компиляции.

  • В зависимости от того, как выкладывается ваш код и объекты в памяти, разделение объектов на отдельные компоненты для каждого домена может дать вам лучшую когерентность кэша и значительно лучшую производительность.

Здесь много больше информации, если вам интересно.

Ответ 2

Возможным недостатком может быть нарушение разделения проблем. Я бы подумал, что в идеале объекты не должны знать, как они визуализируются, так что механизм рендеринга может быть настроен отдельно от игровых объектов... хотя на практике может быть, что эти вещи часто слишком взаимозависимы чтобы быть разделены таким образом.

Ответ 3

Предположим, что каждый раз, когда ваш объект (т.е. игрок) обновляется, он должен

1) перерисовать себя

2) уведомить другие модули, которые он обновил

3) напишите "update-action" в историю/журнал/все остальное (возможно, вы захотите воспроизвести всю игру после ее окончания, например, фильм).

....

n) любое другое взаимодействие между вашим Player-объектом и его средой.

Во всех этих случаях вам придется изменить свой метод обновления, например:

public void update(long timeElapsed) {
   dosmth();

   redraw();
   notifyUnits();
   updateHistory();
}

Это очень раздражает. Поэтому в таких случаях следует использовать шаблон Observer. Ваш объект должен уведомить всех слушателей о том, что он был обновлен. Слушатели (GraphicsContext, History, units) будут реагировать надлежащим образом, и ваша программа будет оставаться легкой в ​​обслуживании, потому что все ее части будут отвечать только за одну конкретную задачу.

Ответ 4

Это нормально, но если у вас много разных объектов, все с спрайтами, позициями и скоростями, которые нуждаются в обновлении, тогда будет много репликации. Вы можете нажать местоположение, скорость в базовый класс, но это жестко прокладывает тип движения. Лучше всего отделить движение от самого объекта.

Например, рассмотрите шар, подпрыгивающий вверх и вниз. Это непросто моделировать только с одной скоростью. Вместо этого создайте класс движения, с ConstantMotiion (для скорости) и BounceMotion для подпрыгивания. Затем класс движения заботится об обновлении состояния позиции объекта от кадра к кадру.

Ответ 5

Полезно взглянуть на возможность того, что большинство объектов в системе должны только модифицировать дескрипторы, специфичные для двигателя (скорость, активная анимация) и позволить движку позаботиться о реальном эффекте. Есть несколько исключений из этого (обычно пули и другие эффекты с одним тиком), но в целом эти системы полагаются на один универсальный тик, который обновляет игру. Причина этого в основном заключается в том, что, как отмечал в своем комментарии к римскому ответу mdma, фактический движок делает намного больше, чем простые операции индивидуального рендеринга. Аналогично, физический движок будет обновлять весь мир, вычисляя объемы столкновений с учетом движения субтипа.

Загрузка спрайта обычно связана с загрузкой всей ценности ресурсов и последующей адресацией этого спрайта по имени в репозитории ресурсов карты.

Столкновение обычно обрабатывается отдельным физическим движком, см. выше.

Ответ 6

Попав за этот шаблон, прежде чем здесь будет мой ввод:

Для игры производительность будет самым большим недостатком. Вероятно, вы хотите сохранить все контексты чертежей, объекты и т.д. В одном месте и оптимизировать один более крупный блок кода.

Если по какой-то причине вы хотите отображать на разных платформах, то этот подход не выполняется из-за отсутствия разделения, снова визуализатор подключаемого модуля является лучшим подходом