XNA 3.1 до 4.0 требует постоянного перерисовки или будет отображать фиолетовый экран - PullRequest
6 голосов
/ 21 ноября 2010

Для меню в моей игре я рисую их один раз на экране, затем перерисовываю, только если они были сочтены грязными. Это выполняется через логическое значение true, когда пользователь выполняет действие, которое должно вызвать перерисовку, и затем цикл рисования проверит это значение перед рисованием меню. Эта логика отлично работала в 3.1, но в 4.0 меню будет мерцать (нарисовано для 1 кадра), затем покажет фиолетовый экран, пока не будет нарисовано снова.

Я создал очень простую тестовую игру в 4.0, чтобы продемонстрировать проблему, показанную ниже. Вы заметите, что экран выглядит фиолетовым. Если вы удалите строку с параметром _isDirty на false, вы увидите синий фон василька.

public class Game1 : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    bool _isDirty = true;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
    }
    protected override void Draw(GameTime gameTime)
    {
        if (_isDirty)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);
            _isDirty = false;
        }
        base.Draw(gameTime);
    }
}

Как мне узнать поведение XNA 3.1? Я видел, как некоторые люди упоминали PreserveContents, но в 4.0 это, похоже, не дает никакого эффекта, если я не применяю его неправильно.

Ответы [ 3 ]

13 голосов
/ 21 ноября 2010

Вот приблизительный обзор того, что называется в игре XNA:

Update
BeginDraw
Draw
EndDraw

Реализация по умолчанию EndDraw в конечном счете вызывает GraphicsDevice.Present. При включенной двойной буферизации это меняет задний и передний буферы.

Итак, что происходит:

  • Первый раз: вы рисуете свою сцену в заднем буфере, а затем позволяете XNA поменять ее на фронт.

  • Второй раз: вы ничего не рисуете, оставляя поверхность, заполненную фиолетовым цветом, к которому DirectX инициализирует эти поверхности, и меняете местами , вперед!

  • В последующие времена: вы ничего не рисуете, поэтому вы увидите мерцание дисплея между этими двумя поверхностями.

Есть несколько способов подавить рисование в XNA. Я просматриваю их в этом ответе на аналогичный вопрос . Как и в этом ответе, я рекомендую переопределить BeginDraw, например так:

protected override bool BeginDraw()
{
    if(_isDirty)
        return base.BeginDraw();
    else
        return false;
}

Когда BeginDraw возвращает false, Draw и EndDraw (и так Present) не будут называться этим кадром. Ничто не будет рисовать, и передний / задний буферы не поменяются местами.

2 голосов
/ 08 июня 2011

Недавно я тоже наткнулся на это; Я не думаю, что это проблема с переключением буферов. Это должно быть то же самое в 3.1 и 4.0. Я посмотрел на GraphicsDevice с ILSpy и обнаружил это:

Что изменилось, так это то, что в 4.0 текущая цель рендеринга сначала очищается в GraphicsDevice.Present (), если установлен частный флаг. По умолчанию этот флаг будет установлен изначально (цель рендеринга чистая). Рисование в цель рендеринга снимает этот флаг (цель рендеринга теперь грязная). После выполнения своих обязанностей GraphicsDevice.Present () снова устанавливает флаг (цель рендеринга была «очищена» от вашего окна и теперь снова считается чистой).

Чтобы получить результаты от GraphicsDevice.Present (), вам необходимо вызывать Draw () и EndDraw () в строгой последовательности.

Если вы вызываете EndDraw () без предварительного вызова Draw (), вы не получите содержимое цели рендеринга (которая очищается), а только фиолетовый экран.

К сожалению, флаг является частным, и нет чистого способа получить его. Вы можете использовать отражение, чтобы очистить флаг следующим образом, заставляя цель рендеринга грязной, не рисуя ничего в нее:

graphicsDevice.GetType (). GetField ("lazyClearFlags", BindingFlags.NonPublic | BindingFlags.Instance) .SetValue (graphicsDevice, 0);

(graphicsDevice - ваш текущий экземпляр GraphicsDevice)

Поместите вышеуказанную строку перед вызовом EndDraw (), и поведение вернется к тому, что было в 3.1

0 голосов
/ 10 ноября 2011

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

Что я сделал, чтобы решить эту проблему, так это просто, в Обновлении, если у меня нет состояния, вызовите метод SuppressDraw () в игровом объекте.Это предотвращает рисование до следующего обновления.

Полагаю, ничто не мешает вам всегда вызывать его в Update, кроме случаев, когда вы устанавливаете флаг isDirty.Но вы должны быть готовы обрабатывать другие события / случаи, когда экран может быть сбит.

...