2012-01-16 3 views
1

Я портирую игру с Ruby на C++. Существует основной цикл рендеринга, который обновляет и рисует контент. Теперь предположим, что во время игры вы хотите выбрать элемент другого экрана. Способ, которым это делается в исходном коде, состоит в том, чтобы сделать Item item = getItemFromMenu();getItemFromMenu - функция, которая откроет меню и будет иметь свой собственный цикл обновления/рендеринга, что означает, что за все время, когда у игрока открылся этот другой экран, вы находитесь в вложенный цикл рендеринга. Я чувствую, что это плохой метод, но я не уверен, почему. С другой стороны, это очень удобно, потому что я могу открыть меню всего одним вызовом функции, поэтому код локализован. Любая идея, если это плохой дизайн или нет? я колебался, чтобы разместить его на игростроения, но так как это в основном дизайн вопрос, который я разместил его здесьНеправильная практика иметь вложенные петли визуализации?

редактировать: некоторые псевдо-код, чтобы дать вам идею:

Обычный цикл в основной части код:

while(open) { 
    UpdateGame(); 
    DrawGame(); 
} 

Теперь внутри UpdateGame() я бы сделать что-то вроде:

if(keyPressed == "I") { 
    Item& item = getItemFromInventory(); 
} 

И getItemFromInventory():

while(true) { 
    UpdateInventory(); 

    if(item_selected) return item; 
    DrawInventory(); 
} 
+1

Я боролся с подобной реальностью в приложении VB.NET, которое выполняет «DoEvents» в виде «окна прогресса» - цикла рендеринга, чтобы избежать зависания и гибели графического интерфейса, а также избежать гадости введения нитей. Это означает, что только одно из моих окон прогресса будет обновляться за раз. Я не против, и он _works_, и он избегает потоков. Но это неприятно. Я оставил его на данный момент ... –

+0

Одна из плохих вещей, которые я мог видеть, - это возможный дублирующий код во всех местах, где у вас такой игровой цикл, в зависимости от того, как это делается. – Xeo

+0

Не могли бы вы показать псевдокод за то, что вы имеете в виду? –

ответ

2

Хороший способ справиться с что-то вроде этого было бы заменить DrawInventory() вызов с чем-то вроде InvalidateInventory(), который будет отмечать текущее графическое состояние запасов как устаревшие и просить его перерисовать в течение следующего рендеринга кадра (что произойдет довольно скоро после того, как основной цикл достигнет DrawGame()).

Таким образом, вы можете продолжать работать по основному контуру, но только те части экрана, которые просматриваются для перерисовки, являются те, которые были признаны недействительными, а во время обычного игрового процесса вы можете аннулировать ваш (2/3) D как обычную часть обработки, но затем внутри инвентаря вы всегда можете отмечать только ресурсы инвентаря, которые необходимо перерисовать, что сводит к минимуму накладные расходы.

Другая часть вашего внутреннего цикла, UpdateInventory(), может быть частью UpdateGame(), если вы используете флаг, указывающий на текущее состояние игры, что-то вроде:

UpdateGame() 
{ 
    switch(gameState) 
    { 
     case INVENTORY: 
      UpdateInventory(); 
      break; 

     case MAIN: 
     default: 
      UpdateMain(); 
      break; 
    } 
} 

Если вы действительно хотите, вы можете также применить это к рисунку:

DrawGame() 
{ 
    switch(gameState) 
    { 
     case INVENTORY: 
      DrawInventory(); 
      break; 

     case MAIN: 
     default: 
      DrawMain(); 
      break; 
    } 
} 

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

0

Что вы создали со своим вложенным циклом рендеринга, является функционально конечным автоматом (поскольку большинство циклов рендеринга игры имеют тенденцию быть). Проблема с вложенным циклом заключается в том, что много раз вам нужно будет делать одни и те же вещи в своем вложенном цикле, как ваш внешний цикл (вход процесса, дескриптор ввода-вывода, обновление отладочной информации и т. Д.).

Я обнаружил, что лучше иметь один цикл рендеринга и использовать конечный автомат (FSM) для представления ваших фактических состояний.Ваши состояния могут выглядеть следующим образом:

  • Главного меню состояние
  • Опция меню состояние
  • Инвентарь состояние
  • Мировоззрение состояние

Вы подключить переходы между состояниями для перемещения между ними. Игрок, нажавший кнопку, может инициировать переход, который может воспроизводить анимацию или иначе, а затем перейти в новое состояние. С FSM вашего цикл может выглядеть следующим образом:

while (!LeaveGame()) { 
    input = GetInput(); 
    timeInfo = GetTimeInfo(); 

    StateMachine.UpdateCurrentState(input, timeInfo); 
    StateMachine.Draw(); 
} 

Полный FSM может быть немного тяжеловеса для небольшой игры, так что вы можете попробовать упрощенную государственную машину с помощью стека игровых состояний. Каждый раз, когда пользователь выполняет действие для перехода в новое состояние, вы нажимаете состояние в стеке. Точно так же, когда они покидают штат, вы выскакиваете его. Только верхняя часть стека обычно получает вход, а другие элементы в стеке могут/не могут рисоваться (в зависимости от ваших предпочтений). Это общий подход и имеет некоторые проблемы и недостатки в зависимости от того, с кем вы разговариваете.

Простейший вариант - просто выкинуть оператор switch для выбора используемой функции рендеринга (аналогично запросу darvids0n). Если вы пишете аркадный клон или маленькую игру-головоломку, которая будет очень хорошо.

Смежные вопросы