[CM]Бест |
Дата: Пятница, 17.01.2014, 15:48 | Сообщение # 1
|
Любитель
Сообщений: 34
Статус: Offline
|
Всем привет с вами [CM]Бест. Вот вся правда о wait 0 Многие из скриптеров, прочитав название статьи, подумают - "А чего там знать? Просто нужно не забывать ставить wait 0 после каждой метки и всё!". Но, к сожалению, они окажутся неправы.
Подавляющая часть скриптеров училась либо по справке к SB, либо у тех, кто учился по этой справке. В конечном итоге, они используют wait 0 после каждой метки или в каждом цикле, ведь так делалось в примерах в справке, а справка "никогда не врёт". Но тут стоит обратить внимание на то, что автор справки (Alexander, насколько я знаю написал большую её часть) сам мало знал о том, для чего на самом деле предназначен опкод задержки.
Давайте попробуем выяснить, для чего же он, на самом деле, существует в языке скриптинга. Для того, чтобы это понять, нам нужно образно представить себе, как работает игра. Многие могут подумать, что всё происходит параллельно - скрипты выполняются сами по себе, физика просчитывается сама по себе, и на экране всё рендерится параллельно всему вышеперечисленному. По крайней мере, так думал я до некоторых пор. На самом деле, такого понятия как параллельность не может существовать в рамках одного процессора (процессорного ядра). Все "движки" игры выполняются последовательно. Если использовать синтаксис SB, то это будет выглядеть примерно так (на номера опкодов внимания не обращаем): Код while true 0001: process_animations 0002: process_physics 0003: process_AI 0004: process_scripting 0005: process_render end
Здесь мы видим бесконечный цикл который содержит последовательное выполнение различных процессов игры. Здесь всё представлено образно, на самом деле всё выглядит немного иначе, но в общих чертах - всё именно так.
Итак, вернёмся к нашему wait 0. Предположим, выполнение кода игры дошло до процесса скриптинга. Наш скрипт начал выполняться. Возьмём такой пример, пока что на основе меток:
Код {$CLEO} 0000: NOP // Чтобы SB не ругался на переход на нулевой оффсет :label_1 if 0AB0: key_pressed 48 then actor.Health($PLAYER_ACTOR) = 100 end wait 0 jump @label_1
Как только наш скрипт начал выполняться, мы попадаем на проверку нажатия клавиши "0". Если клавиша нажата - даём игроку полное здоровье и идём дальше. Если не нажата - просто идём дальше. Дальше у нас стоит wait 0. Обычно его пишут в начале, сразу после метки или перед проверкой, но я специально поставил его в конец. Когда скриптовый процесс достигает опкода wait, он получает задачу переключиться на выполнение следующего скрипта, или, если это был последний скрипт в очереди - закончить скриптовый процесс и приступить к следующему игровому процессу. Как мы все прекрасно знаем, если убрать wait 0 в такой конструкции, то игра зависнет. Вообще, любое зависание любой программы с большой нагрузкой на ЦП - это, чаще всего, попадание в бесконечный цикл. Так и здесь. Так как опкод задержки является своеобразным "сигналом" для скриптового процесса для переключения на следующие процессы игры, то его отсутствие будет равносильно бесконечному выполнению только одного скриптового движка. Проще говоря, скриптовый процесс будет бесконечно проверять, нажата ли наша кнопка, а другие игровые процессы выполняться не будут (такие как рендер новых кадров, например), поэтому нам кажется, что игра зависла, но на самом деле она работает, но расчёты физики моделей, их анимации, а также вывод новых "картинок" на экран не производятся.
"Разве плохо ставить wait 0 после каждой метки?" - спросите вы. Не всегда это плохо. Но давайте разберём случай, когда wait 0 после каждой метки будет лишним. К примеру, нам нужно проверить несколько клавиш, на каждую из которых предусмотрено определённое действие.
Код {$CLEO} 0000: NOP :label_1 wait 0 if 0AB0: key_pressed 48 then actor.Health($PLAYER_ACTOR) = 10 else jump @label_2 end jump @label_1 :label_2 wait 0 if 0AB0: key_pressed 49 then actor.Health($PLAYER_ACTOR) = 20 else jump @label_3 end jump @label_1 :label_3 wait 0 if 0AB0: key_pressed 50 then actor.Health($PLAYER_ACTOR) = 30 else jump @label_1 end jump @label_1
Итак, давайте посмотрим, как будет выполняться наш код. Сперва метка label_1 и wait 0. Пропустим их. Дальше проверка на нажатие клавиши "0" и соответствующее ей действие - установка игроку здоровья равному 10. Если клавиша не нажата - переходим на label_2. Тут у нас снова wait 0, который передаст управление игрой другим процессам, и только после их выполнения вернётся к скрипту, к месту после wait 0. Тут снова проверка на нажатие клавиши и соответствующее действие. Если не нажата - идём к третьей метке. Здесь опять же wait 0, после которого выполнение скрипта прервётся, чтобы выполнить другие процессы и, после этого - вернётся обратно в это же место. Таким образом, прежде чем мы дойдём до проверки третьей клавиши, у нас образуется задержка в 3 общих цикла игры (будем называть их кадрами). Вы скажете - "Но ведь мы написали wait 0, а раз там ноль, значит задержки никакой не будет!". Но даже если там указан ноль, процессор не успеет настолько быстро обработать все остальные процессы игры, чтобы за 0 миллисекунд вернуться обратно к выполнению скриптового процесса. Таким образом, создаётся задержка в несколько миллисекунд - примерно 10-20 мс при 60 фпс. Три таких задержки равны уже 60 мс. А что, если таких проверок много? Придётся зажимать клавишу на полсекунды-секунду, чтобы её нажатие обработалось.
В том, что задержка действительно есть даже у wait 0 мы можем легко убедиться с помощью следующего скрипта:
Код {$CLEO} 0000: NOP while true 33@ = 0 wait 0 0AD1: show_formatted_text_highpriority "%d" time 2000 33@ end
Как мы знаем из той же справки, переменные 32@ и 33@ являются таймерами и изменяют своё значение после каждого цикла (wait 0) на такое кол-во миллисекунд, которое прошло с момента начала прошлого цикла. Перед wait 0 мы обнуляем значение переменной, а после - выводим её значение на экран, получая ту самую задержку, создаваемую опкодом wait 0.
В результате, в скрипте с тремя проверками, выше, можно убрать wait 0 после второй и третьей метки - тогда все проверки выполнятся подряд, без задержек, а после проверок код всё равно вернётся к метке label_1 где стоит wait 0. Таким образом мы убъём двух зайцев сразу - избежим зависания и уберём задержку между проверками.
Подобная проблема может возникнуть при работе с массивами переменных. К примеру, у нас есть массив актёров и нам нужно узнать, у которого из них здоровье не полное и удалить его. В данном случае мы можем применить цикл for, так как он будет весьма удобным решением подобной проблемы с конкретным размером массива. Код for 0@ = 0 to 20 step 1 if actor.Defined($ACTORS[0@]) then 1@ = actor.Health($ACTORS[0@]) if 1@ < 100 then actor.DestroyInstantly($ACTORS[0@]) actor.RemoveReferences($ACTORS[0@]) end end end
Я не вставил в эту конструкцию ни одного wait, так как мы знаем, что цикл НЕ будет выполняться бесконечно, а лишь 20 раз, поэтому шансов на зависание в данном участке кода нет. Если мы вставим в конструкцию wait 0, то "поиск раненого" будет длиться дольше из-за задержек, создаваемых wait 0. Если взять бесконечные циклы, такие как while true, к примеру, то они должны в себе содержать wait 0 - иначе мы снова попадём в зависание.
Сами создатели игры - Rockstar конечно тоже использовали wait 0 по назначению, поэтому можно у них поучиться, посмотрев строение скриптов-потоков в оригинальном main.scm. Таким образом мы должны наблюдать за тем, как выполняется код и следить за тем, чтобы не создавалось бесконечных циклов, в которых скрипт не будет давать возможности выполняться другим процессам игры. В то же время, нужно исключить все лишние задержки, которые могут навредить работе вашего скрипта.
P.S Принимаю спасибо в виде "+" Автор: BoPoH
Спасибо принимаю в репутацию
Сообщение отредактировал [CM]Бест - Пятница, 17.01.2014, 21:24 |
|
|
|
RAKSHA1337 |
Дата: Пятница, 17.01.2014, 16:24 | Сообщение # 2
|
Советник
Сообщений: 784
Статус: Offline
|
[CM]Бест, молодец, научился таки копировать, теперь все уроки от туда скопируешь? p.s. Только не говори что ты сам всё это писал.
В казино рандом. Но рандом в программах - это генератор псевдослучайных чисел, который написан ручками человека. А значит есть формула! Есть алгоритм! Есть тактика! Кто не согласен - ставьте минус, после того как докажете обратное. https://habrahabr.ru/post/196442/
|
|
|
|
[CM]Бест |
Дата: Пятница, 17.01.2014, 16:48 | Сообщение # 3
|
Любитель
Сообщений: 34
Статус: Offline
|
Цитата RAKSHA1337 ( ![Ссылка на цитируемый текст](http://s59.ucoz.net/img/fr/ic/4/lastpost.gif) ) [CM]Бест, молодец, научился таки копировать, теперь все уроки от туда скопируешь? p.s. Только не говори что ты сам всё это писал.
Где ты увидел, что я написал что я сам это написал?
Добавлено (17.01.2014, 16:48) --------------------------------------------- И во вторых чем тебе не нравится, что я копирую там информацию? Информация с любого источника полезна и в любом виде!
Спасибо принимаю в репутацию
Сообщение отредактировал [CM]Бест - Пятница, 17.01.2014, 16:48 |
|
|
|
RAKSHA1337 |
Дата: Пятница, 17.01.2014, 17:04 | Сообщение # 4
|
Советник
Сообщений: 784
Статус: Offline
|
Цитата [CM]Бест ( ![Ссылка на цитируемый текст](http://s59.ucoz.net/img/fr/ic/4/lastpost.gif) ) Где ты увидел, что я написал что я сам это написал? Я не "говорил" того, что ты сам это написал. Цитата [CM]Бест ( ![Ссылка на цитируемый текст](http://s59.ucoz.net/img/fr/ic/4/lastpost.gif) ) И во вторых чем тебе не нравится, что я копирую там информацию? Не нравится то, как ты это подаёшь, оссобенно большие буквы напрягают. Цитата [CM]Бест ( ![Ссылка на цитируемый текст](http://s59.ucoz.net/img/fr/ic/4/lastpost.gif) ) Информация с любого источника полезна и в любом виде! Не согласен. Она не для каждого полезна, плюс чем эта информация старее, тем она менее полезна. p.s. Даже источник не указал. И можно было сократить статью.
В казино рандом. Но рандом в программах - это генератор псевдослучайных чисел, который написан ручками человека. А значит есть формула! Есть алгоритм! Есть тактика! Кто не согласен - ставьте минус, после того как докажете обратное. https://habrahabr.ru/post/196442/
|
|
|
|
[CM]Бест |
Дата: Пятница, 17.01.2014, 17:12 | Сообщение # 5
|
Любитель
Сообщений: 34
Статус: Offline
|
Цитата RAKSHA1337 ( ![Ссылка на цитируемый текст](http://s59.ucoz.net/img/fr/ic/4/lastpost.gif) ) Не согласен. Она не для каждого полезна, плюс чем эта информация старее, тем она менее полезна. p.s. Даже источник не указал. И можно было сократить статью.
Вот как всегда тебе не нравится большой части нравится...
Спасибо принимаю в репутацию
|
|
|
|
Toplivo |
Дата: Пятница, 17.01.2014, 20:59 | Сообщение # 6
|
Советник
Сообщений: 728
Статус: Offline
|
2.2 При копипасте обязательно внизу указывать ник автора, откуда копируете. Например "by АВТОР_СТАТЬИ_КОТОРУЮ_КОПИРУЕТЕ".
Дурак дает,умный берет.
|
|
|
|