|
Содержание
От автораДорогие друзья, перед вами краткое руководство по созданию звуковых игр при помощи BGT (Blastbay Game Toolkit). Это руководство является переводом одного из основных разделов англоязычного руководства, поставляемого в комплекте с движком BGT. Не все возможности языка скриптов BGT отражены в этом тексте, но со временем будет подготовлено полное русскоязычное руководство. Загрузить установочный пакет BGT можно с официальной страницы http://www.blastbay.com/bgt.php. ПредисловиеДействительно ли вы фанат звуковых игр? Ваша голова полна идей, которые вы хотели бы реализовать? Если на оба эти вопроса - ответ "да", тогда продолжаем читать данное руководство. BGT - это новый революционный комплект инструментов, который позволяет Вам создавать ваши собственные звуковые игры с нуля без наличия каких-либо знаний в области программирования. Кто сказал, что создание звуковых игр было и должно быть сложным? BGT- Комплект инструментов для разработки звуковых игр, который принадлежит компании Blastbay, позволяет Вам проводить все ваше ценное время, придумывая новые грандиозные идеи и концепции, а не бороться с бесконечными руководящими принципами программирования, которые мешают приступить к созданию игры. Если Вы вписываетесь в категорию восторженных Разработчиков, то BGT - совершенный инструмент для Вас, превращающий идеи в ошеломляющую действительность. ПроцессПроцесс создания звуковой игры с BGT весьма прост. Прежде всего, удостоверьтесь, что Вы Имеете ясную картину вашей игры. Планируйте её тщательно, или Вы окажетесь в трудном положении позже, когда ваша игра начнёт обретать форму, и Вы внезапно обнаружите, что не знаете, что делать дальше. Не Повторяйте ошибку многих разработчиков - планируйте полный сценарий до начала работы с BGT! Как только в вашем сознании появился полный план игры, начинайте работу! Вам больше не нужны сложные программные инструменты. По существу, всё, в чём Вы нуждаетесь, - редактор текста и BGT комплект, оба из которых Вы уже имеете. Запасайтесь вдохновением, энтузиазмом, творческим потенциалом и спешите вперёд, к созданию игры своей мечты! Внимание! Когда вы пишите сценарий игры, очень важно останавливаться через какое-либо время для запуска и проверки... Таким путем Вы удостоверитесь, что каждая новая особенность, которую Вы добавляете к вашей программе, работает должным образом, и можно приступать к следующему этапу. По завершению написания игры вы сможете скомпилировать её в .exe файл, чтобы передавать её вместе с наборами звуков и инструкций любому пользователю. НачинаемИзучая руководство, приведённое ниже, пробуйте писать программы на каждой стадии обучения, открывая для себя понятия новых функций и значений. Старайтесь начать с малого, постепенно охватывая больше информации. Только при плавном и тщательном изучении BGT, вы сможете избегать множество проблем, и создать достойную игру! Счастливого вам кодирования! 1. ВступлениеДобро пожаловать в краткое руководство по созданию компьютерных звуковых игр на языке BGT! Данное руководство предполагает, что вы ничего не знаете о BGT, но имеете огромное желание научиться создавать ваши собственные игры. Здесь будет охвачено всё от самых основ до более продвинутых аспектов языка. Цель данного руководства научить вас создавать собственные, непохожие не на какие другие звуковые игры, публиковать ваши творения и пополнять ряды разработчиков. 2. Как работает BGT?BGT содержит два компонента:
Мы рассмотрим эти компоненты более подробно. Но сначала Вы должны задаться вопросом, какую же мне создать игру? 2.1. Движок BGTДвижок BGT - это основной компонент. Это - программа, которая читает скрипт и исполняет задачи, написанные Вами. С помощью него вы можете проверять ваши игры. Без движка невозможно было бы заметить и отладить возникшие ошибки в процессе разработки до того момента, пока вы не закончили бы разработку. Движок BGT - это часть программного обеспечения, содержащая основные функциональные возможности для управления звуками, таймерами, файлами и т.д. Он способен читать как один, так и несколько файлов, содержащих программный код вашей игры. Файлы с программным кодом, ещё называемые скриптом или сценарием, содержат инструкции, при чтении которых движок понимает, что ему делать. Например вы можете передать движку инструкцию: играть файл, а по нажатию любой клавиши остановить воспроизведение и выйти из программы. Инструкции записываются, используя специальный набор правил, ещё называемый как синтаксис языка. Это поможет движку понять точно, что от него требуется. Мы начнём изучать эти правила в последующих разделах данного руководства. 2.2. Компилятор BGTКомпилятор BGT - это вторая часть программного обеспечения, используемая для преобразования скрипта вашей игры в отдельную программу. Только вы и подобные вам люди знают о том, что перед использованием вашей игры сначала нужно установить на компьютер движок BGT. Конечно же, вы можете вкладывать в архив с игрой и инсталляционный пакет движка, но это не самый лучший вариант. Бывает так, что ваш скрипт может содержать важную информацию, пути к файлам и тому подобную информацию, которую пользователю знать необязательно. Открытость скрипта позволит пользователю свободно изменять код вашей игры. Вот тут на помощь к нам приходит компилятор, преобразующий все файлы скриптов в одну исполняемую программу и скрывающий весь код. И конечный пользователь может спокойно пользоваться вашим творением, не беспокоясь о том, что происходит за кулисами. 3. Синтаксис языкаДостаточно предисловий, начнём изучение. Имеются некоторые пункты, которые мы должны рассмотреть, прежде чем начнём изучение языка программирования: 3.1. ОператорыОператоры - это отдельная инструкция, при чтении которой BGT получает информацию о последующих действиях. Каждый оператор отделяется специальным знаком точки с запятой ";". Этот знак служит для отделения одного оператора от другого и передачи управления от оператора к оператору. Вы можете писать каждый оператор с новой строки, просто ставя в конце предыдущего знак точки с запятой. Это намного улучшит читабельность вашего кода. Например: int main() { return 0; } В данном примере мы использовали всего лишь один оператор return, который должен вернуть какое-то значение после работы функции. О функциях мы поговорим в следующих главах. 3.2. БлокиЧтобы сделать ваш код более удобочитаемым как для вас, так и для движка BGT, мы используем блоки. Блок - это группа операторов, ограниченная специальными знаками. Блоки кода ограничиваются фигурными скобками, то есть {и}. Например: int main() { return 0; } Вы видите блок кода, состоящий из одной строки. Мы не принимаем во внимание первую строку, так как она говорит о начале функции, о которых мы поговорим в следующих главах. Далее идёт блок, который отделён фигурными скобками. 3.3. КомментарииБывает так, что после окончания разработки какой-либо игры, через много месяцев вы снова захотите вернуться и продолжить начатую работу. Но вот как понять, что и к чему относится в ранее написанном вами же коде? А если ваш код будут читать другие программисты? Для предотвращения случаев путаницы в коде, непонимания его частей были придуманы комментарии. Комментарии бывают двух типов. 1. ОднострочныйОднострочный комментарий - это любой текст, предварённый знаком // (две косых черты), и длящийся до конца строки. Всё, что следует с новой строки, будет восприниматься как код. Такие комментарии используются для краткого описания функций, проверяемых условий, указания в начале файла имени разработчика, даты разработки и т.д. Например: // это первая строка комментария... // а это ещё одна строка... 2. Многострочный комментарийМногострочный комментарий - это любой текст, занимающий несколько строк. Текст комментария помещается между знаками /* и */ Всё, что следует после знака /*, будет проигнорировано движком, а всё, что находится за */, будет восприниматься как код. Обычно такие комментарии используются для подробного описания функций, классов и т.д. Например: /* это многострочный комментарий */ 4. Печать текста на экранеПо устоявшимся традициям, любое практическое программирование начинается с программы, выводящей текст на экран. На первый взгляд, это очень просто, но пример такой программы позволяет понять синтаксис языка и принцип его действия. Ознакомьтесь с первым примером. void main() { alert("Привет", "Я скрипт на языке программирования BGT!"); } На первый взгляд, этот пример может вас слегка запутать. Давайте разберём его подробнее. void main() Это сообщает компилятору, что мы используем функцию с именем main, которая всегда является первым блоком кода, выполняющимся при запуске скрипта. Мы рассмотрим функции более подробно в последующих разделах данного руководства, но на данном этапе при создании любого скрипта вы должны запомнить, что эта функция должна присутствовать обязательно. { Как вы уже знаете, блоки кода отделяются друг от друга фигурными скобками. Тоже самое касается и функций. alert( Это имя функции, поддерживаемой BGT, выводящей на экран простое сообщение с заголовком и некоторым текстом. Когда вы вызываете функцию, имеющую несколько параметров, то все эти параметры перечисляются внутри круглых скобок через запятую. "Hello", Это первый параметр, означающий заголовок окна сообщения. Если этот параметр оставить пустым, то есть ничего не писать между кавычками, то заголовок останется пустым. Когда вы пишите текст подобно этому, вы всегда должны окружать его с двух сторон кавычками, в противном случае компилятор воспримет ваш текст как часть кода и попытается его выполнить. "Я скрипт на языке программирования BGT!"); Это второй параметр, определяющий текст сообщения, который должен будет выведен в основном окне сообщения. Правая круглая скобка означает окончание вызова функции, а точка с запятой окончание оператора. } Это сообщает компилятору, что мы закончили нашу главную функцию. Взгляните на этот пример ещё раз. void main() { alert("Привет", "Я скрипт на языке программирования BGT!"); } Теперь вам должно быть немного понятно. Если же это не так, то вернитесь к началу данной главы и прочтите её несколько раз до тех пор, пока вы твёрдо не усвоите материал. 5. ПеременныеПеременная - это область оперативной памяти, выделенная компьютером и содержащая в себе определённое значение. Размер выделяемой памяти зависит от типа создаваемой переменной. В большинстве случаев переменные требуют явной инициализации. Инициализация - присвоение определённого значения созданной переменной. например: x = 0; В нашем случае переменная может использоваться для хранения значения здоровья игрового персонажа. BGT поддерживает пять основных типов переменных:
5.1. Объявление переменныхПеред тем как создать переменную, вы должны определиться с её типом, после чего указать этот тип. Каждая переменная должна иметь своё имя, оно указывается после типа. Обратите внимание: имя переменной может содержать символы от a до z , числа и знак подчеркивания. оно не должно начинаться с числовых символов. Рассмотрим пример: string winning_message; Это сообщает BGT, что мы хотим создать переменную строкового типа (string), присвоить ей имя winning_message. Точка с запятой после имени обозначает окончание выражения. Теперь мы имеем неинициализированную строковую переменную и должны присвоить ей значение. Это можно сделать, задав значение на той же строке после знака равно, и взяв текст в кавычки. например: string winning_message="Поздравляем!Вы выиграли игру!"; 5.2. IntПеременные целочисленного типа содержат в качестве значения целые числа. Они не могут содержать десятичные дроби и тем более строки. С ними можно выполнять следующие действия: умножение, деление, сложение и вычитания. BGT при работе с такими переменными также поддерживает более сложные математические функции, возможно, что когда-нибудь они вам понадобятся. Целочисленные переменные можно использовать так: int apples=5; int bananas=2; int oranges=8; int fruit_basket=apples+bananas+oranges; Здесь Вы видите четыре переменные. Первую называем apples (яблоки), вторую - bananas (бананы), третью - oranges (Апельсины), а последнюю - fruit_basket (корзина с фруктами), в которую мы сложили 3 предыдущие. В своих примерах вы можете использовать так же круглые скобки. здесь они служат не для обозначения параметров функции, а для указания порядка действий так же, как это делается в математических выражениях. Демонстрируем вам ещё один пример использования целочисленных переменных. Он не имеет никакого практического использования; только показывает гибкость числовых переменных и как они могут использоваться, чтобы исполнить вычисления, не входящие в регулярную математику. int x=3; x=x*3; int y=8; int z=x/y; z+=x-1; x*=z+4; Выражения типа x+=1 эквивалентны выражениям типа x=x+1, и используются для краткой записи подобных выражений. Имеется один дополнительный арифметический оператор, который Вы можете использовать. Это оператор деления по модулю. Обозначается он знаком процента (%) и может также использоваться одновременно с операцией присваивания (то есть со знаком = ). Этот оператор вычисляет остаток при делении одного целого числа на другое. Приведем пример : int x=11; x%=3; В этом случае x будет равен 2, так как 11 разделенное на 3 равняется 3 и 2 в остатке. 2 - это результат действия оператора деления по модулю. Имеются ещё два оператора, которые часто используются в циклах. Они известны как оператор декремента (обозначается знаками --)и оператор инкремента (обозначается знаками ++). Когда мы пишем следующую строку: x++; Мы сообщаем BGT, что нужно увеличивать x на 1. Такая операция полезна для экономии времени и ускорения программы. Будьте осторожны с диапазоном чисел, разрешенных типом , назначенным переменной. Ниже следует список типов и разрешенного диапазона для их значений.
5.3. float и doubleКлючевое слово float обозначает простой тип, используемый для хранения 32-разрядных значений с плавающей запятой, которые ещё называются вещественными значениями. Переменные такого типа могут содержать в себе как отрицательные, так и положительные числа. Ниже следует список поддерживаемых типов для таких значений.
Если вы не знаете, какого вида число будет содержаться в вашей переменной, то лучше всего использовать тип double, так как этот тип поддерживает самый большой диапазон. Следует заметить, что если же число превышает допустимый диапазон, то компилятор выдаст предупреждение. Приведём пример чисел, которые могут содержаться в переменных типа float. float x=1.5; float y=0.236; float z=-85.156; 5.4. boolПеременные типа bool, принимают всего 2 значения:
При создании переменной типа bool она по умолчанию содержит значение false. Такие переменные чаще всего используются как переключатели, индикаторы состояния (поэтому иногда их называют флагами) и используются при проверке условий. Например, вам нужно проверить, открыта ли дверь. Вот так бы это выглядело в коде: bool door_opened=false; if(door_opened==false) { // дверь заперта. } if(door_opened==true) { // Можно входить. } Если надо проверить переменную на true, то для сокращения условия можно писать вот так: if(door_opened) { // Можно входить. } А если переменную надо проверить на false, то условие можно сократить так: if (!door_opened) { // Можно входить. } В этом фрагменте используется логическая операция "не", которая обозначается восклицательным знаком (!). Ниже следует таблица логических операций и значений, получаемых при работе с этими операциями. Логическая операция И (AND)Обозначение AND: && Логическая операция И выполняется с двумя переменными, назовем их a и b. Результат выполнения логической операции И будет равен true, если a и b равны true, а во всех остальных (других) случаях, результат будет равен false. Посмотрите пример истинности логической операции and.
Логическая операция ИЛИ (OR)Обозначение OR: || Логическая операция ИЛИ выполняется с двумя переменными (a и b). Результат выполнения логической операции ИЛИ будет равен false, если a и b равны false , а во всех остальных случаях результат равен true. Посмотрите пример истинности логической операции OR.
Логическая операция НЕ (not)Обозначение NOT: ! Эта логическая операция выполняется с одной переменной. Результат выполнения этой логической операции напрямую зависит от состояния переменной. Если переменная находилась в состоянии false, то результат выполнения NOT будет равен true, и наоборот. Посмотрите пример истинности логической операции НЕ.
5.5. StringПеременные строкового типа (string) очень отличаются от переменных числовых типов которые мы рассмотрели в предыдущих главах. Они очень полезны, и скоро вы в этом убедитесь. Переменная типа string (строка) может содержать в себе как строку текста, так и отдельные символы. Следовательно, строковые переменные могут содержать, например, имя игрока, путь к файлу на жёстком диске, содержимое текстового файла и т.д. Когда вы присваиваете такой переменной какое-либо значение, не забывайте окружать текст с двух сторон кавычками. Это нужно для того, чтобы компилятор видел, где находится код, а где текст, который обрабатывать ненужно. В главе 4, где мы учились выводить текст на экран, мы использовали переменные строкового типа. Далее мы вызывали функцию печати текста на экране и в круглых скобках писали между кавычками либо текст, который нужно было напечатать, либо просто указывали имя переменной, содержащей какой-то текст. Ниже приведён пример инициализации строковой переменной и печати текста на экране. string my_name="John Doe"; alert("Меня зовут", my_name); Как вы видите, в функции печати текста на экране мы заполнили первый параметр, отвечающий за заголовок окна простым текстом, а в качестве второго параметра мы передали имя переменной, содержащей в себе текст, присвоенный в начале программы. Почему же мы не взяли my_name в кавычки? Если бы мы сделали это, тогда на экране вместо текста, содержащегося в переменной, напечаталось имя самой переменной. Давайте немного разнообразим наш предыдущий пример. string my_name="John Doe"; string message_string="Меня зовут " + my_name + " и не забудьте это!"; alert("Важная информация", message_string); Вы спросите, что означает + my_name +? Данная операция просто вставляет содержимое строковой переменной в то место строки, где она была указана. Так что ваше сообщение будет следующим: Меня зовут John Doe и не забудьте это! Таким образом в строку можно вставлять содержимое не только строковых переменных. Вы убедитесь в этом, посмотрев на следующий пример. string my_name="John Doe"; int age=23; string message_string="Меня зовут " + my_name + ". Мне " + age + " года и не забудьте это!"; alert("Важная информация", message_string) Ваше сообщение будет выглядеть так: Меня зовут John Doe. Мне 23 года и не забудьте это! Вы заметили, как мы вставили в строку содержимое переменной целочисленного типа? Любая переменная с любым типом может быть вставлена в текст, если её содержимое может быть преобразовано в строку. Конечно, вы можете написать сообщение непосредственно при вызове функции печати на экран, не создавая отдельной переменной. Выглядеть это будет так: string my_name="John Doe"; int age=23; alert("Важная информация", "Меня зовут " + my_name + ". Мне " + age + " года и не забудьте это!"); Результат будет таким же. Отличие только в количестве строк кода. Давайте ещё более разнообразим наш пример. string my_name="John Doe"; int age=random(5, 50); string message_string="Меня зовут " + my_name + ". Мне " + age + " Не забудьте это!"; alert("Важная информация", message_string); При инициализации целочисленной переменной мы использовали функцию random, поддерживающейся BGT, которая генерирует случайное число в диапазоне, указанном нами. Каждый раз, когда мы будем запускать эту программу, она будет выдавать разные числа. Теперь давайте рассмотрим способ сложения строк. string string1="Я "; string string2="строка!"; string string3=string1 + string2; Первым двум переменным мы присваиваем части строки, а в третью последовательно складываем эти части. При печати содержимого третьей переменной на экране результат будет таким: Я строка! Вы можете выполнять со строковыми переменными точно такие же действия сложения, как и с целочисленными переменными. Посмотрите на этот пример: string my_string="Привет "; my_string+="мой друг."; Естественно, теперь переменная my_string будет содержать строку "Привет мой друг". Существует ряд специальных знаков, которые могут быть включены в текст. Чтобы компилятор понимал, что эти знаки не следует обрабатывать как строку, нужно записывать эти знаки после символа обратной косой черты \. Ниже следует список специальных знаков:
string string1="Это\tстрока,\tиспользующая\tтабуляцию."; string string2="Это\r\nновая\r\nстрока."; string string3="Путь к моей игре: \"c:\\program files\\bgt\\my_game\\my_script.bgt\""; Заметьте, как используются специальные знаки для форматирования текста и печати специальной информации. 5.6. ConstКонстанта, инициализированная в начале программы, в отличие от переменной, не может изменять своё значение в ходе выполнения. Цель использования констант - в удобочитаемости и экономии времени. Чтобы объявить константу, перед указанием типа переменной вы пишете ключевое слово const. Посмотрите на следующий пример. const string snd_ext=".wav"; sound gun; sound beep; sound ambience; sound music; gun.load("sounds/gun"+snd_ext); beep.load("sounds/beep"+snd_ext); ambience.stream("sounds/wind"+snd_ext); music.stream("sounds/music"+snd_ext); Преимущество здесь заключается в том, что если ваша игра стала намного больше и вы решили конвертировать все звуки в .ogg формат, то вам нужно всего лишь изменить значение константы с .wav на .ogg Некоторые функции BGT в качестве параметров принимают константы, а при их вызове на месте этого параметра нужно указать имя константы. Например, при вызове функции открытия файла в качестве параметра, отвечающего за метод открытия, удобней писать write чем 2. Это облегчит запоминание методов открытия файлов. То же самое может быть и в других функциях, в том числе и в ваших. 6. ФункцииВы уже сталкивались с несколькими примерами вызова функций. В главе 4 вызывалась функция alert, выводящая текстовое сообщение на экране. Самое время начать продвижение в изучении функций, так как без них не обходится ни одна игра. Функция - это блок кода, который может быть вызван по имени. Напомним, что имена в скриптах BGT могут содержать лишь латинские буквы, цифры и знак подчёркивания. Например, вы можете написать функцию с именем jump, отвечающую за прыжок игрока. Всякий раз, когда вы хотите, чтобы игрок подпрыгнул, вы вызываете функцию jump и код внутри этой функции выполняется. Функции оказывают большую помощь не только в структурировании вашего кода, но они так же помогают в многократном использовании каких-либо частей кода без нужды писать одинаковый код в разных частях программы. Все функции, которые мы вызывали до сегодняшнего дня, входили в состав движка BGT, но вы так же можете создавать свои собственные функции. Главное, что вы должны запомнить - это указывать функцию main, так как без неё компилятор не примет ваш скрипт. Эта функция является точкой входа в программу. В ней происходит инициализация переменных, загрузка звуков, показ экрана программы и т.д. Мы уже встречались с этой функцией в главе 4. Давайте посмотрим на следующий пример. void main() { alert("Тест", "Сейчас мы используем функцию main()"); } Данную функцию нельзя вызывать в любом месте программы. слово "void" указывает на то, что функция ничего не возвращает. Возвращение данных функциями мы обсудим позже в этой главе. Каждая функция имеет собственное имя. Часто функции могут принимать некоторые параметры, указывающиеся в круглых скобках после имени функции. Функция main не принимает никаких параметров, и поэтому список её параметров пуст. Давайте рассмотрим пример функции, принимающей в качестве параметров минимальное и максимальное число, и возвращающей сумму этих чисел. void main() { int x=add_numbers(3, 5); alert("Вау", "3 + 5 равняется... " + x + "!"); } int add_numbers(int first, int second) { int result=first+second; return result; } В этом примере возникают новые для вас моменты, которые необходимо понять. Прежде всего вы должны были заметить, как мы вызвали функцию сложения чисел из главной функции. В качестве параметров ей были переданы числа 3 и 5. Вы можете подставлять любые числа. В заголовке функции add_numbers между круглыми скобками указываются две целочисленных переменных с именами first и second, означающие первое и второе число. Когда вы вызываете эту функцию, вы должны указывать 2 параметра. Первое число присвоится первому параметру, а второе второму. Вы можете называть параметры функции любыми именами. Внутри функции создаётся целочисленная переменная, в которой будет сохранён результат сложения чисел , полученных через первый и второй параметры. И в конце, используя оператор return мы возвращаем результат действия функции и выходим из неё. Возвращённый результат записывается в ранее созданную переменную x и тут же печатается на экране. Надеемся, вы так же заметили, что мы начали функцию add_numbers не со слова "void", а со слова "int". Слово "void" означает, что функция ничего не возвращает, а в нашем случае функция возвращает целое число, поэтому мы и перед именем функции указываем её тип как целочисленная "int". Как в случае с переменными, объявление функций так же начинается с объявления её типа. Далее мы даём нашей функции имя, желательно такое, чтобы вы потом смогли по названию понимать, что выполняет эта функция. После имени функции, в круглых скобках мы через запятую перечисляем параметры функции, если они есть. Затем внутри фигурных скобок пишется тело функции. Посмотрите на следующий пример. void main() { alert("Wow", "3 + 5 is... " + add_numbers(3, 5) + "!"); } int add_numbers(int first, int second) { return first+second; } Этот пример аналогичен предыдущему. Единственное что мы сделали, это не создавали специальную переменную для хранения результата, а вызвали функцию сложения внутри списка параметров функции вывода сообщения на экран. 7. Операторы условияОчень часто в процессе выполнения программы возникают ситуации, имеющие несколько решений. В таком случае программа должна выбрать одно из решений. Вот тут на помощь к нам приходят операторы условия. При выполнении определённого условия мы можем позволить программе выбрать одно из решений, указанных в теле оператора. Например, вы хотите, чтобы сердце персонажа стало биться чаще, если показатель здоровья меньше 20 процентов. В этом случае мы должны проверить, действительно ли здоровье игрока меньше 20 процентов, и при положительном результате ускорить сердцебиение. 7.1. ifКонструкция if является одной из ключевых во многих языках, в том числе и в BGT. Она позволяет выполнять фрагменты кода при выполнении условия. BGT предлагает структуру if, которая аналогична такой же структуре языка C, C++ и т.д. if(health < 21) { // играть звук сердцебиения } Компилятор проверяет, действительно ли переменная health меньше 21, и если это так, то выполнится код между фигурными скобками. В противном случае этот код будет проигнорирован. if(health < 21) { // играть звук сердцебиения } else { // делать что-нибудь, если здоровье не меньше 21 } В данном случае условие имеет две ветви. Если здоровье игрока меньше 21, то будет выполнен код между первой парой фигурных скобок, а блок, следующий за оператором else будет проигнорирован. Если же здоровье игрока не меньше 21, то будет выполнен код между второй парой фигурных скобок, а код, находящийся перед оператором else будет проигнорирован. // простое условие с использованием фигурных скобок if(health<21) { // играть звук сердцебиения } // простое условие без использования фигурных скобок if(health<21) // играть звук сердцебиения // составное условие с использованием фигурных скобок if(health<21) { // играть звук сердцебиения alert("Alert!", "You are about to die..."); //показ сообщения } else { // делать что-нибудь } Важно помнить, что в составных условиях всегда используются фигурные скобки для отделения одной ветви от другой. Рекомендуется использовать фигурные скобки во всех видах условий. Ниже предлагается список операций сравнения:
Давайте рассмотрим следующий пример: string my_name="John Doe"; int age=random(5, 50); string message_string="Меня зовут " + my_name + ", Мне " + age + " лет и не забудьте это!"; alert("Важная информация", message_string); А теперь добавим проверку возраста. string my_name="John Doe"; int age=random(5, 50); string message_string="Меня зовут " + my_name + ", Мне " + age + " лет и не забудьте это!"; if(age<18) { message_string+="Мне меньше 18 лет!"; } else { message_string+="я совершеннолетний!"; } alert("Important information", message_string); Вышеуказанный пример действительно простой, но мы разберём всё по частям. Сначала мы создаём строковые переменные my_name и message_string, а также целочисленную переменную age, в которую будет записываться случайное число от 5 до 50 с помощью функции random. Далее мы проверяем age (возраст). Если он меньше 18, тогда прибавляем к текстовому сообщению фразу мне меньше 18 лет, а если возраст больше 18, тогда добавляем слова я совершеннолетний. Затем выводим сообщение на экран. Конструкции if могут вкладываться друг в друга, главное, следить за правильностью вложений. 7.2. switch и caseswitch и case являются заменой конструкции if и используются для составления более сложных условий. Рассмотрим пример. int health; double energy; switch(health) { case 100: energy=100; break; case 90: energy=89; break; case 80: energy=78; break; case 70: energy=67; break; case 60: energy=56; break; } Ключевое слово switch объявляет оператор условие, а внутри круглых скобок размещается значение (метка), Которую программа будет искать внутри тела условия. Тело условия размещается внутри фигурных скобок. Здесь может быть использовано множество операторов выбора case, так как это просто различные варианты условия, но работать будет тот, который равен определённому значению. В паре с каждым оператором case используется оператор break для остановки проверки условия и выхода из него. Если опустить этот оператор, то проверка условия будет продолжаться непрерывно, пока не достигнет конца тела условия. Также в теле условия мы используем ключевое слово default, содержащее в себе значение, которое будет использовано, если никакое из предыдущих решений условия не подошло. Давайте рассмотрим следующий пример: switch(health) { case 100: energy=100; break; case 90: energy=89; break; case 80: energy=78; break; default: energy-=0.5; } Теперь, если здоровье игрока меньше 80 и продолжает уменьшаться, то энергия будет уменьшаться на 0.5. Вы можете использовать оператор default в теле условия только один раз. Конструкции case могут вкладываться друг в друга. 8. ЦиклыВы уже умеете составлять как простые, так и составные конструкции условных операторов. Пришло время познакомиться с новым понятием, таким как цикл. Цикл - часть кода, которая будет выполняться много раз до тех пор, пока некоторое условие не перестанет быть истинным. Циклы могут использоваться в процессе чтения файла, проверке некоторых специфических действий и т.д. Циклы делятся на 3 вида, каждый из которых мы рассмотрим в этой главе. 8.1. Цикл whileРассмотрим следующий пример. while(key_pressed(KEY_ESCAPE)==false) { wait(5); } Вышеуказанный пример может показаться вам сложным, но не волнуйтесь, мы разберём его подробно. В первой строке мы имеем ключевое слово while, которое означает начало цикла. Затем в круглых скобках указывается условие, которое будет проверяться при каждой итерации (то есть повторе) цикла. В нашем случае мы используем функцию key_pressed для проверки, была ли нажата клавиша. Цикл будет выполняться до тех пор, пока не будет нажата клавиша, указанная в условии. Как только условие станет ложным, а это произойдёт, когда key_pressed() вернёт true, программа выйдет из цикла и продолжит своё выполнение. Левая и правая фигурные скобки определяют тело цикла. Функция внутри тела цикла означает паузу в 5 миллисекунд между каждой итерацией. Представим, что перед вами стоит задача выполнить некое действие тысячу раз. Взгляните на следующий пример цикла, который сделает тоже самое в разы быстрее. int x=0; while(x<1000) { x++; } Сначала мы создаём целочисленную переменную x и присваиваем ей значение 0. Затем в мы объявляем цикл и в круглых скобках указываем условие x<1000. В теле цикла мы прибавляем к x 1. Цикл будет выполняться до тех пор, пока x не станет равным 1000. Посмотрите на ещё один пример, в котором показано, как с помощью цикла будут выведены числа от 0 до 10 на экран. int x=0; string numbers=""; while(x<=10) { numbers+=x; if(x<10) { numbers+=" "; } x++; } alert("Printing numbers 0 to 10", numbers); Цикл будет выполняться до тех пор, пока x не будет равен 11. 8.2. Цикл do whileВ прошлой части главы о циклах вы изучили цикл while. Теперь мы можем рассмотреть практически похожий цикл doo while. Давайте рассмотрим пример, в котором программа ожидает нажатие клавиши escape. do { wait(5); } while(!key_pressed(KEY_ESCAPE)); Первая строка сообщает о начале цикла. Далее идёт тело цикла. А после правой фигурной скобки располагается условие в круглых скобках, которое будет проверяться после каждой итерации цикла. Если вы заметили, то перед условием стоит восклицательный знак, означающий повторение цикла, пока значение, возвращаемое функцией key_pressed((), ложно... Нижеследующие примеры идентичны предыдущему. while(key_pressed(KEY_ESCAPE)==false) while(!key_pressed(KEY_ESCAPE)) Заметьте, что в последней строке первого примера после правой круглой скобки стоит точка с запятой. Она служит для того, чтобы после проверки условия, если оно не выполняется, продолжить выполнение программы. Имеется одно известное различие между while и doo while циклами. В цикле while сначала проверяется условие, и если оно ложно, то весь код цикла полностью пропускается. А в цикле doo while сначала выполняется код цикла, а за тем проверяется условие. 8.3. Цикл forНаиболее используемый вид цикла for. Главным образом используется для создания счётчиков. В данном цикле комбинируются 3 типа контроля цикла. Вспомните циклы while и doo while. Прежде чем мы начинали цикл, мы объявляли переменную и присваивали ей определённое значение. В нашем случае это был 0. Далее шло объявление цикла и условие в круглых скобках. А в теле цикла, перед правой фигурной скобкой мы изменяли значение созданной переменной для следующей итерации. В цикле for все эти 3 действия собраны в одну строку. for(int x=0; x<=10; x++) { // код цикла } Рассмотрим каждую часть по отдельности. Сначала мы создаём переменную и присваиваем ей начальное значение. Эта переменная будет доступна и в теле цикла. Далее мы указываем условие, которое должно проверяться при выполнении цикла. В нашем случае цикл будет продолжать выполняться, пока x меньше 10. И далее увеличиваем x на 1, для того чтобы цикл снова проверил условие и, в зависимости от результата, повторился или нет. for(x=1; x<1000; x*=2) { // код цикла } Теперь каждый проход цикла будет увеличивать значение x в 2 раза. 8.4. Операторы break и ContinueПришло время поговорить об операторах break и continue. Часто при возникновении некоторого события удобно иметь возможность досрочно завершить цикл. Используемый для этой цели оператор break (разрыв) вызывает немедленный выход из циклов, организуемых с помощью операторов for, while, do-while, а также прекращение оператора switch. Взгляните на следующий пример. while(true) { if(something==true) { break; } if(something_else==true) { break; } if(yet_another_variable==true) { continue; } // код цикла wait(5); } Запомните, что если циклы вложены, оператор break осуществит выход только из цикла, в котором он вызван. Оператор continue, который вы встретили в третьем условии, тоже предназначен для прерывания циклического процесса, организуемого операторами for, while, do-while. Но, в отличие от оператора break, он не прекращает дальнейшее выполнение цикла, а только немедленно переходит к следующей итерации того цикла, в теле которого он оказался. Он как бы имитирует безусловный переход на конечный оператор цикла, но не за пределы самого цикла. 9. МассивыДо сих пор мы пользовались простыми переменными, которые могли содержать в себе одно значение. Но рано или поздно даже в средней игре вы столкнётесь с такой проблемой, как огромное количество практически одинаковых переменных. Представьте, что вам нужно создать поле в 50 квадратов, а за тем проверить позицию игрока. Пусть каждый квадрат будет иметь 2 значения:
Взгляните на следующий пример. int board0=2; int board1=2; int board2=2; int board3=2; int board4=1; int board5=2; int board6=2; int board7=2; int board8=2; int board9=1; int board10=2; int board11=1; int board12=2; int board13=2; int board14=1; Давайте остановимся... Теперь вы согласны, что код невероятно похож и утомителен в написании? А если бы уровень был бы в 300 квадратов... Вообразите радость выписывать 300 таких же переменных. К счастью, имеется достаточно легкое решение ... Массивы. Массив, подобно любой другой переменной, содержит значения, но, в отличие от неё, может содержать несколько значений, обратиться к которым можно по индексу. Массив имеет своё имя. Массив может быть такого размера, который вам нужен. Взгляните на пример. int[] board(50); for(int x=0; x<50; x++) { board[x]=2; } Здесь мы снова встречаем много нового. Давайте, как всегда, разбирать всё по строкам. Первая строка сообщает компилятору о том, что нужно создать массив из значений типа int. В круглых скобках указано количество элементов массива - 50. Квадратные скобки после int сообщают компилятору, что эта переменная является массивом. Массиву дано имя, для того чтобы потом обращаться к нему, а между квадратными скобками указывать индекс элемента, с которым будем работать. В следующей строке мы начинаем цикл, внутри которого объявляем переменную x, а в теле цикла в качестве индекса элемента указываем этот x. Здесь же каждому следующему элементу мы присваиваем значение 2. Цикл будет перебирать массив до тех пор, пока x не станет равным 49. Почему 49? Элементы массива всегда начинают индексироваться с нуля. Поэтому если ваш массив содержит 50 элементов, то первый будет иметь индекс 0, а последний 49. Если же цикл выйдет из указанного диапазона, то возникнет ошибка выполнения. Поэтому рекомендуется проверять игру после каждого нового добавления массива. В последней строке мы сообщаем об окончании цикла. После того как цикл полностью выполнен и значение 2 назначено всем элементам, мы можем вызывать каждый отдельный элемент по его индексу и назначать ему любое другое значение. Вы можете использовать как одномерные, так и многомерные массивы, например, для создания x y z сетки. Например, если вы хотите создать шахматную доску, то вы бы писали следующее: int[][] chessboard; Многомерный массив, проще говоря, это массив в массиве. Для доступа к элементам в многомерном массиве вы используете тот же метод, что был указан выше, только теперь вы указываете не один, а 2 индекса. Индекс элемента в первом измерении и индекс элемента во втором. Например доступ к квадрату в левом верхнем углу вы пишете вот так: chessboard[0][0]=5; А доступ к нижнему левому углу вы записываете вот так: chessboard[0][7]=5; То же самое происходит и с массивами с тремя измерениями. BGT максимально поддерживает четырёхмерные массивы. 10. Классы и объектыКлассы и объекты являются одними из значимых звеньев в создании звуковых игр на BGT, поэтому рекомендуем прочитать эту главу несколько раз для полного усвоения материала. Объект - это переменная, но со специальными функциональными возможностями. Вы можете думать об объекте как о переменной, которая содержит другие переменные и функции (методы) внутри себя. Чтобы вам стало понятно, взгляните на первый пример. sound ambience; ambience.load("curry.ogg"); Данная программа делает две вещи. В первой строке мы создаём переменную типа sound, поддерживаемого BGT и даём ей имя. А во второй строке мы вызываем для созданной переменной метод загрузки звукового файла, в качестве параметра передавая имя файла на диске. Тип sound, как и остальные особые типы, с которыми вы познакомитесь, были уже написаны и включены в BGT. Нам же остаётся создавать нужное количество переменных того или иного типа, и вызывать для них нужные методы, принадлежащие этим типам. Иногда при создании объекта может понадобиться список параметров, определяющих начальные свойства объекта. Объект так же может и не принимать никаких параметров, например, объекты типа sound и timer при создании не принимают никаких параметров. Объект типа timer был бы создан вот так: timer jumptime; Как только объект был создан, вы можете начинать использовать его. Объект имеет свойства, которые являются преимущественно переменными, содержащимися внутри объекта, хранящие различные данные в течение срока существования объекта. Например, объект типа sound имеет свойства volume (громкость). Оно определяет громкость звучания и его можно изменять в реальном времени. Посмотрите пример, в котором показано создание объекта типа sound, открытия в нём звукового файла как поток и установка громкости. sound ambience; ambience.stream("curry.wav"); ambience.volume=-6; Довольно простой пример, верно? Конечно же, можно составлять условия для проверки состояния свойств. if(ambience.volume>-10) { // делать что-нибудь } В данном случае если громкость больше -10, тогда выполнится тело условия. Некоторые свойства не могут быть изменены, а только проверены. Метод - это просто функция подобно любой другой, которые вы создаёте или используете, с единственным различием в том, что они работают исключительно с конкретным объектом, созданным вами. Если опять же говорить об объекте типа sound, то у него имеется один из часто использующихся методов play_wait, который приостанавливает выполнение программы на время воспроизведения звука, а за тем снова возвращает управление программе. Фактически методы load и stream могут использоваться вместе. Давайте немного расширим наш пример. sound ambience; ambience.stream("curry.wav"); ambience.volume=-6; ambience.play_wait(); Вам уже знаком этот пример. Единственное, что здесь добавилась строка, которая говорит компилятору о воспроизведении ранее загруженного в объект звукового файла и приостановку выполнения программы. Конечно же, вы можете создавать свои объекты и классы с их собственными методами и свойствами. Например, вы могли бы создать класс врага следующим образом: class enemy { int health; int speed; int position; void fire_weapon() { // код оружия } void move(int direction) { // код передвижения } } Единственная новая строка для вас это первая. Но она просто сообщает компилятору о создании класса с именем enemy. Сделано это с помощью ключевого слова class. В нашем случае вражеский класс имеет три свойства: health (здоровье), speed (скорость) и position (позиция), и два метода: fire_weapon(выстрелить)и move (переместиться). Однако, мы не должны напрямую изменять переменную position, так как метод move() будет делать это за нас. Чтобы использовать наш класс, мы писали бы вот так: enemy robot; Тогда наш класс и объект этого класса готовы к использованию, как классы и объекты, поддерживаемые BGT. robot.move(right); // указание движение вправо с помощью константы robot.fire_weapon(); //использование оружия роботом. Имеется небольшая проблема. Она выражается в установке здоровья нашему роботу. Каждый раз, при создании подобного объекта мы должны присваивать переменной health (здоровье) 100. Это легко решается с помощью функций деструктора и конструктора. Конструктор представляет собой метод класса, который облегчает вашей программе инициализацию элементов данных класса. Конструктор имеет такое же имя, как и класс, и не имеет возвращаемого значения. Каждый раз, когда ваша программа создает переменную класса, BGT вызывает конструктор класса, если конструктор существует. Многие объекты могут распределять память для хранения информации; когда вы уничтожаете такой объект, BGT будет вызывать специальный деструктор, который может освобождать эту память, очищая ее после объекта. Деструктор имеет такое же имя, как и класс, за исключением того, что вы должны предварять его имя символом тильды (~). Деструктор тоже не имеет возвращаемого значения. Давайте ещё немного расширим наш пример. class enemy { int health; int speed; int position; enemy() { health=100; position=0; speed=300; } void fire_weapon() { // код оружия } void move(int direction) { // код передвижения } } Теперь, когда мы создаём новый объект типа enemy, сразу же будут устанавливаться его свойства, согласно конструктору. Обратите внимание, что также возможно писать следующее: class enemy { int health=100; int speed=300; int position=0; void fire_weapon() { // код оружия } void move(int direction) { // код передвижения } } В данном случае мы не используем конструктор, а присваиваем значения переменным непосредственно при их объявлении. Однако всё же это работа конструктора, и поэтому в дальнейшем мы будем придерживаться этого правила. Чтобы сделать ваш класс более компактным, вы можете использовать параметры для конструктора. Взгляните на этот пример. class enemy { int health; int speed; int position; enemy(int init_health, int init_speed, int init_pos) { health=init_health; position=init_pos; speed=init_speed; } void fire_weapon() { // код оружия } void move(int direction) { // код передвижения } } Теперь, чтобы объявить переменную типа enemy, мы напишем следующее. enemy robot(100, 300, 0); Это установит свойства нашего объекта так, как мы их объявили. Таким образом вы можете создавать врагов с разным уровнем здоровья, скоростью и позицией. Обратите внимание, мы создаём конструктор, принимающий параметры, а следовательно, мы не сможем создать ещё объект класса enemy без указания его свойств. Для того чтобы создавать объекты класса enemy с установленными свойствами или нет, вы можете создать несколько конструкторов в вашем классе. С деструкторами делать подобное нельзя. Посмотрите на этот пример. class enemy { // Свойства объекта // конструктор без параметров enemy() { health=100; position=0; speed=300; } // конструктор с параметрами enemy(int init_health, int init_speed, int init_pos) { health=init_health; position=init_pos; speed=init_speed; } // методы класса } Теперь, чтобы объявить переменную типа enemy, вы можете писать так: enemy robot; или enemy robot(200, 150, 0); Первое объявление создало бы объект со свойствами, установленными в первом конструкторе, а вот во втором вы самостоятельно устанавливаете свойства для создаваемого объекта. Обратите внимание, что переменные-свойства так же требуют явной инициализации внутри класса, как и обычные переменные. Если же вы объявите объект, например, так: enemy robot; То, благодаря конструктору по умолчанию, свойствам этого объект не грозит остаться неинициализированными. Деструктор работает так же, как конструктор, за исключением того, что он вызывается, когда происходит удаление объекта из памяти. Обычно это может происходить при выходе из программы или в процессе игры, если это объект поверженного врага. Немного изменим наш пример. class enemy { // свойства объекта enemy() { health=100; position=0; speed=300; } ~enemy() { alert("Поздравляем", "Ваш враг уничтожен"); } // методы класса } Если бы вы использовали этот пример в вашей игре, то при выходе из неё вы получили бы сообщение о том, что ваш враг уничтожен. Происходит это, потому что при выходе программа уничтожает созданный объект. Поэтому в теле деструктора вы тоже можете явно указать, что делать при уничтожении объекта этого класса. 11. Подключение дополнительных скриптовОдна из полезных функций BGT заключается в способности объединять несколько файлов с исходным кодом в одну программу. Это очень удобно в том случае, когда вы пишете большую игру и вам нужно просмотреть код какой-нибудь отдельной функции. Поэтому мы можем создавать отдельные файлы для главного меню, игрока, управления звуком, уровнями игры и т.д. Чтобы подключить файлы скриптов в один главный файл игры, используйте следующее выражение: #include "scriptname.bgt" Когда компилятор встречает данную команду, он просто считывает содержимое файла, имя которого указано в кавычках, как будто он полностью скопирован в главный файл. Важно заметить, что функция main в подключаемых файлах присутствовать не должна. Она используется только в главном файле, как точка входа в программу. Также важно помнить правила записи пути к файлу. Если вы записываете имя файла так, как указано в примере, то компилятор сначала будет искать этот файл в том же каталоге, где находится главный файл. Если компилятор не найдёт подключаемый файл в этом каталоге, тогда он попытается найти его в каталоге include, идущим в комплекте с движком BGT. Ну а если же файл не будет найден и там, тогда компилятор сообщит об ошибке. Конечному пользователю не нужно самостоятельно подключать дополнительные файлы скриптов, так как все они будут упакованы в исполняемую программу. 12. ЗаключениеПодведём итоги. С помощью данного руководства вы обучились основам создания игр на BGT. Теперь вы знаете, как работает движок и компилятор. Узнали типы переменных и метод их создания. Научились выводить текст на экран средствами BGT. Выяснили, как подключать дополнительные файлы скриптов к основному файлу игры. Узнали, что такое функция, условное выражение и цикл. И, наконец, научились работать с массивами и классами объектов. Теперь вы имеете достаточный набор знаний для того, чтобы начать создавать свои, пусть пока простые звуковые игры. Рекомендуем вам возвратиться и снова перечитать те главы руководства, которые вызывают у вас вопросы. Творческих успехов! ПриложенияП.1. Клавиатурные константыДанные константы используются в связке с функциями key_down и key_pressed для проверки нажатия какой-либо клавиши.
П.2. Коды ошибокДанные константы, возвращаемые функцией get_last_error, дают более подробную информацию об ошибках, возникших в ходе выполнения.
П.3. Константы даты и времениДанные константы используются для сообщения текущей даты и времени на системе пользователя во время выполнения сценария.
|
|||||||||||||||||||||||||||||||||||||||||||||
Распространение материалов сайта означает, что распространитель принял условия лицензионного соглашения. Идея и реализация: © Владимир Довыденков и Анатолий Камынин, 2004-2024 |
Социальные сети