Игровой цикл вылетает из моего браузера - PullRequest
0 голосов
/ 29 февраля 2012

Я пытаюсь написать свою первую игру на html5.Однако игровой цикл приводит к тому, что мой браузер перестает отвечать на запросы (в конечном итоге он отключается браузером).Я создал конечный автомат:

while(state != State.EXIT){
  switch(state){
    case State.SPLASH:
        break;
    case State.HOW_TO:
        break;
    case State.PLAY:
        oldTime=Date.now();
        state=gameLoop();
        break;
    case State.GAME_OVER:
        break;
    default:
        state=State.EXIT;
    }
}

Кажется, все работает нормально.Итак, вот игровой цикл:

function gameLoop(){
var newTime=Date.now();
var delta=newTime-oldTime;

update(delta/1000); 
render();

oldTime=newTime;

return state;
}

Здесь происходит сбой.Если я уберу оператор return, он вернет ноль или любой другой javascript.И это нормально.Он запускается один раз и выходит.Однако, если я оставлю это там, это то, где браузер захватывает.Функция обновления дает моему персонажу возможность двигаться, а функция рендера рисует одно изображение на экране.Очень простые вещи.

ПРИМЕЧАНИЕ. Это записывается в элементе canvas, если это имеет значение.

РЕШЕНИЕ!Я создал функцию stateSelector (), которая содержит оператор switch выше (без while).Однако вместо state = gameLoop я использовал интервал = setInterval (gameLoop, 1).Затем я использую clearInterval (интервал), когда хочу остановиться, после чего сразу следует stateSelector ().Очевидно, что если я хочу изменить состояние, я делаю это перед вызовом функции stateSelector.Я мог бы, вероятно, принять параметр, содержащий состояние, в которое я хочу войти, но это небольшое изменение, которое я мог бы оценить позже.Я просто хотел объявить о своем решении на случай, если кто-нибудь еще столкнется с этим.

Ответы [ 4 ]

3 голосов
/ 29 февраля 2012

JavaScript является однопоточным и работает (в действительности) в потоке GUI во всех распространенных средах браузера.Когда вы используете JavaScript, пользовательский интерфейс браузера не обновляется до тех пор, пока не завершится работа JavaScript.

Вы используете цикл while, который никогда не завершится, и поэтому пользовательский интерфейс никогда не будет обновляться.Чтобы это исправить, вам нужно немного реструктурировать: визуализировать кадр, а затем сообщить браузеру, что вы хотите визуализировать другой кадр в ближайшее время;он может обновлять пользовательский интерфейс и выполнять другие действия, связанные с просмотром, а затем может возвращать вас для визуализации другого кадра.

Реализация

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

  • mozRequestAnimationFrame для Gecko (Firefox)
  • webkitRequestAnimationFrame для WebKit (Chrome и Safari)
  • msRequestAnimationFrame для Trident (Internet Explorer)

Так что, если доступен префикс requestAnimationFrame, используйте его.Если это не доступно, но с префиксом, используйте это.Если ничего из этого не работает, вы можете использовать запасной вариант:

function fallbackRequestAnimationFrame(func) {
    setTimeout(func, 10); // Schedule func to be run in 10 milliseconds.
}

Вот слегка измененная версия кода , найденная в MDN :

var myRequestAnimationFrame =
       window.requestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.msRequestAnimationFrame
    || fallbackRequestAnimationFrame;

Один разВы выяснили, какую функцию requestAnimationFrame вы можете использовать, вы можете изменить свой игровой цикл (который, кажется, не функция gameLoop, в которой нет циклов, а скорее цикл while), чтобы он выглядел следующим образом:

function runFrame() {
    switch(state) {
        // state handling code
    }
    if(state != State.EXIT) {
        myRequestAnimationFrame(runFrame);
    }
}

Затем запустите:

runFrame();
0 голосов
/ 29 февраля 2012

JavaScript выполняется в том же потоке, который браузер использует для визуализации страницы, поэтому, если вы пишете бесконечный цикл, браузер никогда не получит обратно контроль для обновления страницы.(Современные браузеры обнаруживают «длительные» циклы и предлагают пользователю возможность их прервать, но это не поможет вам в вашей игре.)

Вам необходимо использовать либо setTimeout(), либо setInterval() снекоторые изменения в следующем:

function gameLoop() {
   // do calculations
   // render
   // etc

   if (!gameOver)
       setTimeout(gameLoop, 30);
}

gameLoop();

(Примечание: ваша оригинальная функция gameLoop() фактически не зацикливается - ваш цикл управляется вне функции - тогда как то, что я только что показал , делает loop.)

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

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

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

0 голосов
/ 29 февраля 2012

Как правило, программисты делают циклы приятными, добавляя вызовы sleep(s) или yield(), но, поскольку в модели, управляемой событиями, в javascript их нет, вместо этого вы должны заменить цикл на setInterval(), который может вызывать функцию, содержащую что-то вроде вашеготело цикла каждый указанный интервал, скажем, каждые 33 миллисекунды, для 30 кадров в секунду.

0 голосов
/ 29 февраля 2012

Я думаю, что вам может понадобиться какая-то пауза, если вы зацикливаетесь без паузы, она будет потреблять весь процессор, обрабатывающий цикл снова и снова, предотвращая отрисовку страницы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...