Продолжение рубрики по программированию на BlitzBasic. Последний выпуск - см. N 99.
Но сначала - ответы на задачки юбилейного выпуска. На самом деле, там требовалась лишь внимательность, и все. Никакого понимания смысла кода не нужно, достаточно лишь повнимательнее вглядеться в синтаксис.
На первое задание правильно ответил Алексей (первым) и еще два человека, а на второе (даже более простое :), увы, никто.
Насчет 2-й ничего не понял :)
в 1-й (про ракеты) не знаю, что там могло в коде глючить,
но единственное на что обратил внимание, это
использование break и continue.
break приводит к выходу из switch и переходит к следующему оператору цикла for.
continue переходит к следующему оператору цикла for , то есть делает то же самое.
И зачем тогда надо было и тот и другой в коде писать, непонятно.
Наверно лучше было только continue использовать,
так как break может приводить и к выходу из switch и к выходу из for
В первом задании в оператор for вложен оператор switch, внутри которого задействован оператор break. Увы, но break всегда прерывает ТОЛЬКО один ближайший цикл/переключатель. В Паскале, кстати, в операторе case не надо использовать никаких break-ов, а вот в Си пропуск break-а в операторе switch приводит к самым печальным ошибкам.
То есть: оператор break прервет лишь работу оператора switch, но не работу внешнего оператора for (хотя программист по привычке будет подразумевать связку break - for), и оператор цикла будет в результате выполнять оператор switch бесконечно (ну, до срабатывания ветки с оператором return).
Кстати, хороший компилятор выявит такую особенность и на последний оператор
return sum;
выдаст предупреждающее сообщение "Unreachable code" (Недостижимый код).
Выполнение функции target_recognize никогда не закончится - опраторы прерывания цикла break относятся согласно синтаксису Си прежде всего к оператору выбора switch. Однако выход из этого оператора по команде break (или continue) не вызывает прерыания бесконечного цикла for(). Для корректной работы данной функции необходимо заменить операторы "break;" на, например, операторы "return sum;".
Второе задание.
Ошибка допущена в оперторе
else if( *VelD ) /* чтобы не делить на ноль */
*VelL = *SRS/*VelD /* уменьшить скорость под
профиль */ ;
Символы "/" и "*" трактуются компилятором в формуле *SRS/*VelD как НАЧАЛО КОММЕНТАРИЯ, а НЕ как операция деления "/" и обращение по адресу "*". Это начало стандартных комментариев - пара символов "/*". Настоящий комментарий начинается немного позже, но программист ошибся именно в данном месте, перепутав два символа с комбинацией начала комментария. В результате величина *VelD в формуле деления не учитывается.
Чтобы избежать данной ошибки, рекомендуется в процессе компиляции отключать режим допустимости вложенных комментариев.
Правильный вариант записи этого оператора такой:
else if( *VelD ) /* чтобы не делить на ноль */
*VelL = *SRS / (*VelD) /* уменьшить скорость под
профиль */ ;
В подобных нюансах и заключается опасность языка Си (как и сила :).
Да пребудет с вами Сила Си! :-)
BlitzBasic. Анимированные спрайты.
Предварительно - несколько дополнительных возможностей BlitzBasic, которые часто востребованы.
Вывод текста
Функция вывода текста на экран Text применяется достаточно часто, но не столько в интересах игрового дизайна, сколько в целях вывода временных сообщений, интересных в основном разработчику. Дело в том, что данная функция рисует строку в заданной позиции экрана одним из стандартных шрифтов Windows и не умеет делать красивые графические надписи. Вообще универсального средства вывода каких-то оригинальных графических текстов не существует, поэтому реализовывать ее обычно приходится вручную.
Команда Text имеет такие параметры: координаты начала выводимой строки (x и y), сама строка, и два необязательных логических параметра. Первый из них, будучи установлен в True, указывает, что текст в заданных позициях надо выводить не с начала, а центрировать по середине.
Следующий пример
Graphics 800,600,16 while not KeyHit(1)
Text 400,100,"Привет тебе!",True,False
Wend
выводит на экран строку, которая выравнивается по позиции 400 (центр экрана) так, чтобы ее середина соответствовала 400-му пикселу.
Измерение времени
Важная функция Millisecs() без параметров выдает текущее время по системному таймеру, выраженное в милллисекундах. С помощью этой функции обычно строятся различные схемы управления частотой показа кадров и анимированных элементов. Основываются они на следующем алгоритме.
В некоторую переменную заносится текущее время:
FirstTime = Millisecs()
Реальное значение времени при этом знать не требуется. Далее, по ходу работы программы текущее время сравнивается со значением этой переменной, и если разница больше некоторого порога (в миллисекундах, тысячных долях секунды), выполняется новое действие.
Например, мы хотим после старта программы пять секунд показывать одно сообщение, а потом - другое:
Graphics 800,600,16
SetBuffer BackBuffer()
FirstTime = Millisecs()
while not KeyHit(1)
Cls
If (Millisecs()-FirstTime) < 5000 then
Text 400,100,"Привет тебе!",True,False else
Text 400,100,"Время вышло.",True,False end if
Flip
Wend
Схожая схема применяется во многих механизмах формирования анимированных изображений. Вывод конкретного кадра мультипликации всегда требуется синхронизировать с каким-то временным промежутком (если показывать кадры без задержки друг за другом, то они могут просто слиться в слишком быструю анимированную последовательность). Следующий алгоритм показывает, как можно задавать требуемое число кадров в секунду (точнее, нужную задержку между кадрами в миллисекундах):
Graphics 800,600,16
SetBuffer BackBuffer()
FirstTime = Millisecs()
N = 0
while not KeyHit(1)
Cls
if MilliSecs() > FirstTime + 250 then
FirstTime = Millisecs()
N = N + 1 if N >= 12 then N = 0
end if
; рисуем N-й кадр
; ...
Text 400,100,N
Flip
Wend
Здесь переменная N хранит номер условного кадра, который в текущий момент времени должен выводиться на экран.
Величина 250 - число миллисекунд (четверть секунды), через которые должны сменяться кадры. Как только этот порог превышен, переменная FirstTime вновь получает значение текущего таймера, а значение номера кадра увеличивается на 1 и при необходимости корректируется (в примере принято, что число кадров изменяется в диапазоне от 0 до 11). Два оператора модификации значения N можно записать и так:
N = (N + 1) Mod 12
где Mod - операция Бейсика по вычислению остатка от деления.
Анимированные спрайты
Мы рассмотрели возможность создания спрайтов и вывода их на экран. Но отрисовка статичных спрайтов, состоящих из одной картинки, полезна ограниченно. Востребована она в основном в задачах вывода курсора, отображения неподвижного игрового поля и т. д. А при создании игр обычно все динамические элементы сцены постоянно меняют свой внешний вид.
В BlitzBasic имеется поддержка вывода так называемых анимированных спрайтов - картинок, которые состоят из набора кадров, и в каждый момент времени показывается один определенный кадр из этого набора. Вот как может выглядеть содержимое такого спрайта -
прямая ссылка.
Необходимо, чтобы кадры следовали слева направо друг за другом без разделяющего пространства. Размеры всех кадров должны быть равны. Так, если размер нашего кадра будет равен 32*32 пиксела, то размер данной картинки (ее надо подготовить заранее и сохранить например в файле myaminspr.bmp) составит 160 пикселов (32*5 кадров) в ширину и 32 пиксела в длину.
Как обычно, черный цвет по умолчанию считается в картинке цветом прозрачности и на экран не выводится.
Загрузка анимированного спрайта
Чтобы указать программе, что загружается анимированный спрайт, надо выполнить команду
LoadAnimImage (filename,width,height,first,count)
Здесь filename - строка, задающая путь к нашей картинке с кадрами, width и height соответственно - ширина и высота каждого кадра (не всей картинки, а одного кадра!), first - номер кадра из набора, с которого начнется анимация (нумерация кадров начинается не с единицы, а с нуля!), и count - число кадров в загружаемой картинке (в нашем случае - 5).
Значение данной функции LoadAnimImage (идентификатор спрайта) надо сохранить в переменной, как и в случае с LoadImage().
Вывод на экран
Вывод анимированного спрайта на экран осуществляется уже знакомой функцией DrawImage, только она дополняется новым параметром - номером показываемого кадра спрайта. Таким образом, вышеприведенную программу отображения кадров через промежуток времени можно расширить анимированным спрайтом так:
Graphics 800,600,16
SetBuffer BackBuffer()
aspr = LoadAnimImage ("anispr.bmp",32,32,0,5)
FirstTime = Millisecs()
N = 0
while not KeyHit(1)
Cls
if MilliSecs() > FirstTime + 100 then
FirstTime = Millisecs()
N = (N + 1) mod 5
end if
Text 400,100,N
DrawImage aspr, 400, 200, N
Flip
Wend
В переменной aspr хранится идентификатор спрайта, смена кадров происходит через 100 миллисекунд (десять раз в секунду). Значение N обнуляется через пять шагов, так как в нашем спрайте пять кадров.
Задание.
Модифицируйте данную программу так, чтобы она показывала кадры не через заданные промежутки времени, а максимально возможное число раз (без пауз). Дополните ее функцией подсчета и показа текущего числа кадров в секунду (так называемый показатель FPS, frames per sec, характеризующий производительность компьютера и графической платы).
Добавьте в программу несколько других спрайтов с разным числом кадров.