ИнформАрхив QSP

Учебник по AeroQSP и QGen

Примечание Aleks Versus: это статья об устаревшем плеере AeroQSP; данный плеер больше не работает в связи с прекращением поддержки и распространения Adobe Flash Player; статья представлена для ознакомительных целей, так как в ней достаточно полно отражены основные принципы написания игр на QSP.

В этом учебнике рассмотрены основные приемы программирования на языке QSP с использованием AERO Shell (QSP плеер на flash). Пособие ориентировано в первую очередь на тех авторов, которые только начинают знакомство с языком, но может быть частично полезно и тем, кто переходит на разработку под AERO с «классического» плеера. Следует помнить, что это разные варианты плеера, хотя и совместимые, но не полностью.

Для работы вам потребуются два приложения: собственно проигрыватель AERO, в котором будет запускаться ваша программа (далее «плеер» или «проигрыватель») и утилита редактирования .qsp файла - QGen (далее «редактор»).

Начнем с ознакомления с их интерфейсом. Откройте редактор. Слева вы видите окно со списком локаций. Если вы еще не начинали никакого проекта, оно должно быть пустым. Если там что-то есть, можно начать заново, нажав "Ctr+N" или выбрав пункт меню "Игра > Новая".

Давайте создадим первую локацию и заодно узнаем, что это за зверь и зачем он нужен. Для создания локации нажмите "F7" или кликните на большой зеленый крестик в левой части верхнего меню. Вы увидите диалог, который предложит ввести название локации. Назовем ее "Начало".

Теперь, в главном окне должно появиться разбитое на четыре части поле. Сверху-слева «Описание:», правее «Выполнить при посещении:» и снизу два окошка озаглавленные «Базовые действия:». Основным инструментом является поле «Выполнить при посещении:». Остальные два носят вспомогательную функцию и лучше даже не приучаться их использовать – это может немного облегчить процесс поначалу, но затем сделает ваш код менее стройным и более фрагментированным. Далее мы будем писать код исключительно в окошке «Выполнить при посещении:». Вы можете смело развести его границы на максимум и забыть про остальные поля.

Сверху над полем «Выполнить при посещении» вы можете увидеть ярлычок-закладку с названием локации. Если вы откроете несколько локаций одновременно, то все они появятся там в виде подобных ярлычков, нажимая на которые вы сможете видеть код выбранной локации.

Что же такое эта самая «локация»? Если вы немного владеете английским, то вероятно уже поняли, что это калька с английского "location" – т.е. "место". Такое название появилось благодаря принятой в "IF" (Interactive Fiction, Интеракивная Литература) практике разбиения кода на отдельные части, в каждой из которых находится кусок кода отвечающий за функционирования той или иной зоны игрового мира. Однако, не стоит путаться и воспринимать слово "локация" прямо. Следует понимать, что это всего лишь отдельный кусок программы который может выполнять абсолютно любую функцию.

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

Чтобы начать ознакомление с интерфейсом проигрывателя, нам надо написать программу, которую мы могли бы в нем запустить. Отдавая дань традиции, принятой в большинстве пособий по программированию для начинающих, давайте заставим программу выводить на экран фразу "Hello world".

Сделать это очень просто. Напишите в первой строчке следующее:

'Hello world!'

Как вы можете видеть, программа автоматически нумерует строки кода. Это сделано для удобства и систематизации кода. Кроме того, если в коде будет ошибка, то в сообщении об ошибке программа скажет, в какой именно локации и строке этой локации возникла проблема.

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

Теперь программу можно запустить прямо здесь, не выходя из редактора. Для этого необходимо нажать "F5" или кнопку в виде синей стрелки в верхнем меню. Если вы еще не указывали редактору путь к проигрывателю, то он попросит вас сделать это.

И, если все было сделано верно, то после короткой заставки "QSP" перед вами должен появиться серый экран с надписью "Hello world!" в верхнем углу. Это значит, что наша программа работает, а мы можем пока рассмотреть интерфейс проигрывателя.

Тут все так же довольно просто. Сверху есть ниспадающее меню всего с двумя пунктами «Игра» и «Помощь». Содержимое пунктов так же не блещет разнообразием. Если нажать «Esc», то выйдет меню сохранения игры. Да, QSP позволяет сохранять игру практически в любой момент, без каких либо специальных усилий со стороны автора самой игры – эта возможность вшита в оболочку.

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

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

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

Еще ниже, расположена строка ввода. Если вы ткнете туда мышкой, то появится курсор и можно будет набрать какой-нибудь текст. Этот вариант используется преимущественно для так называемых «парсерных» игр, где управление происходит при помощи текста вводимого игроком. Но рассматривать способы создания парсерных игр в этом руководстве мы не будем, так что о строке ввода вы можете пока забыть.

В правом нижнем углу осталось еще одно свободное поле. Это поле дополнительного описания. Обычно оно используется для вывода текущего состояния или характеристик персонажа, но, как и остальные поля, может быть использовано другими, порой неожиданными, способами.

Следует понять, что хотя у всех указанных полей и есть довольно четко очерченная основная функция, вы можете использовать их так, как удобно вам и нужно для целей вашей игры. Более того, размер и расположение данных полей не являются неизменными. Вы спокойно сможете переместить или отключить любое из них в коде игры. Вы даже можете задать свои собственные поля, если это потребуется. То что вы видите – просто удобный шаблон, который может подойти для среднестатистической игры в жанре "IF".

Сейчас наша «игра» выглядит пустой, серой и неинтересной, так что плеер можно закрыть и вернуться в редактор. Давайте сделаем что-то более серьезное. Но для начала разберем уже имеющуюся программу.

Как вы можете видеть, слова заключенные в одинарные кавычки выделены зеленым цветом. Цветовая разметка кода в QGen сделана для вашего удобства и она действительно помогает читать код. Забегая слегка вперед, скажем, что переменные в коде будут иметь черный цвет, операторы – синий, а текст комментариев – серый.

Если вы напишите слова Hello world без кавычек, они останутся черными и при попытке выполнить программу, плеер выдаст ошибку, так как не воспринимает эти слова как [выражение]. Слова, заключенные в кавычки, являются выражением – т.е. воспринимаются не как осмысленный код, а как некое значение, с которым программа будет работать. Например, текст, который нужно вывести на главный экран.

Одинарные и двойные кавычки работают одинаково. Выражения 'Hello world!' и "Hello world!" будут интерпретироваться одинаково. Но что будет, если мы заходим вывести на экран такую фразу например: «Программа сказала "Hello world!"»?

Если использовать только двойные кавычки, то программа нас не поймет.

Код "Программа сказала: "Hello world!" " будет интерпретироваться неверно.

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

Поэтому код 'Программа сказала: "Hello world!" ' программа воспримет правильно.

Вы можете сделать наоборот и поменять местами двойные и одинарные кавычки:

"Программа сказала: 'Hello world!' "

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

Но давайте перейдем к более интересным вещам и попробуем создать настоящую игру. Процесс этот не слишком быстрый, но чрезвычайно увлекательный. Я шаг за шагом приведу вам пример разработки несложной игры, в которой будут задействованы все самые распространенные возможности QSP. Не стесняйтесь модифицировать код приведенных здесь примеров и экспериментировать с ним по ходу чтения руководства.

Итак, начнем новый проект с чистого листа. Первым делом, снова создадим локацию под названием «Начало». Это будет стартовая локация игры. Я задумал воплотить в интерактивном тексте обычную московскую квартиру и представить главным героем игры программиста-фрилансера. Эта концепция уже дает нам основание для создания нескольких локаций. Давайте, не мудрствуя лукаво, сделаем по одной локации на каждое помещение в квартире.

Создаем новые локации под названиями: «Комната», «Коридор», «Ванная», «Туалет» и «Кухня». Теперь, у нас суммарно есть целых шесть локаций, но все они пустые и никак не соединены между собой. Займемся этим.

Начнем с комментария. Если вы поставите восклицательный знак в начале строки, то вся строка следующая за ним окрасится в серый цвет. Это текст комментария, который программа не пытается интерпретировать. Он нужен только для того, чтобы оставить пояснения в тексте кода, в качестве напоминания себе самому, или же разъяснения тем, кто будет разбирать ваш код позднее.

Откроем локацию «Начало» и напишем там комментарий:

! Это стартовая локация, в которой начинается игра

Не самый нужный комментарий, но сойдет. Кстати, если вам нужно будет сделать комментарием несколько строк кода то, вовсе не обязательно ставить ! в начале каждой строки. Можно сделать вот так:

!'Тут комментарий

И его продолжение

И еще немного'

Таким образом вы закомментировали все выражение целиком (т.е. все то что было заключено в кавычки, сразу после восклицательного знака). Учтите, что восклицательный знак обозначает комментарий, лишь если стоит первым символом в строке. В остальных случаях, не заключенный в кавычки ! эквивалентен утверждению «не равно». Т.е. код a ! b , например, означает, что переменная «а» не равна по значению переменной «b». Никакими комментариями тут и не пахнет.

Но вернемся к игре.

Следом за комментарием, с новой строки напишем следующее:

'МОЯ ПЕРВАЯ ИГРА'

ACT 'Начать игру':
    GT 'Комната'
END

Что делает первая строка должно быть понятно и так. А вот суть строчек идущих за ней интереснее. Мы только что создали первое действие. Оператор ACT собственно и говорит о том, что перед нами действие. Выражение, следующее сразу за ним в кавычках – это название действия, так, как его увидит игрок. Двоеточие тоже необходимо – оно означает, что весь код идущий дальше вплоть до закрывающего оператора относится к тому, что надо будет сделать, если игрок выберет данное действие в меню. END в конце — это и есть тот самый закрывающий оператор. Он указывает то место, в котором заканчивается код, относящийся к действию «Начать игру».

Между ними идет строчка, в которую уместилась собственно суть действия. Оператор GT это сокращение от GOTO (вы можете писать и целиком, программа поймет). Как легко поймет любой знакомый с английским читатель, это команда перехода куда-то. Куда? На другую локацию, название которой и следует сразу за оператором. Помните мы создали локацию «Комната»? Так вот, это действие заставит программу перейти к тому куску кода, который находится в локации «Комната». Можно условно считать, что это герой игры переместился в комнату своей квартиры.

Если запустить код сейчас, то вы увидите слова «МОЯ ПЕРВАЯ ИГРА» в виде заголовка в главном окне описаний и пункт «Начать игру» в меню действий. Теперь, нажав на него, мы можем переместиться в «комнату». Но что это? Перед нами девственно чистый экран! Ну разумеется, ведь локация «Комната», к которой перешла программа, абсолютно пуста.

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

Напишем в соответствующих локациях код:

'КОМНАТА'

ACT 'Идти в коридор':
    GT 'Коридор'
END
'ВАННАЯ'

ACT 'Идти в коридор':
    GT 'Коридор'
END
'ТУАЛЕТ'

ACT 'Идти в коридор':
    GT 'Коридор'
END
'КУХНЯ'

ACT 'Идти в коридор':
    GT 'Коридор'
END

И наконец для коридора:

'КОРИДОР'

ACT 'Идти в комнату':
    GT 'Комната'
END

ACT 'Идти в ванную':
    GT 'Ванная'
END

ACT 'Идти в туалет':
    GT 'Туалет'
END

ACT 'Идти на кухню':
    GT 'Кухня'
END

Вуаля! Теперь у нас есть целая виртуальная однокомнатная квартира, по которой можно бродить до бесконечности. Запустите плеер и проверьте как это работает.

Надоедает такое занятие, конечно, мгновенно. На самом деле, не умея делать ничего кроме переходов по локациям, уже можно создать игру. Но арсенал, который предоставляет нам QSP, неизмеримо больше – иначе не стоило бы и огород городить. Так что мы не остановимся на достигнутом и попробуем оживить пространство.

Как насчет индивидуальных описаний каждой зоны?

Это проще простого. Можно просто добавить в наш код описания, как мы уже делали раньше. Например, для комнаты пусть это будет так:

'Захламленное помещение размером 3x4 метра. Эта комната не знала ремонта со дня новоселья. Да и уборки тут не было уже очень-очень давно. Но зато тут есть главное – рабочий компьютер с выходом в Интернет, спальное место и тумбочка, в которой лежат деньги! '

Добавьте это описание в локацию «Комната» и место сразу станет немного интересней. Попробуйте сами придумать описание для остальных помещений. А после этого, мы двинемся дальше.

Итак, мы написали, что в тумбочке лежат деньги. Обманывать нехорошо, так что придется их туда положить. Как это сделать?

Для этого надо будет использовать один из важнейших инструментов QSP (да и программирования в целом) – так называемые «переменные». Умное определение переменной вы найдете в справке по QSP, а я просто покажу, как это работает. Итак, в наш игровой мир должно прийти неизбежное зло – деньги. Создадим переменную деньги. В отличие от некоторых других языков программирования, в QSP нам не надо объявлять переменные заранее. Любая переменная, значение которой не было указано раньше, считается существующей, но равной нулю. Так что мы можем сразу переходить к коду.

Назовем деньги, которые лежат в кармане героя игры, просто «деньги», а те что отложены в тумбочку «заначка». Это собственно и будут наши переменные. Сейчас они обе равны нулю – денег нет. Чтобы в заначке что-то было надо добавить такой код:

заначка = 100

Таким образом, мы присвоим переменной «заначка» значение 100. В мире игры же это будет значить, что в тумбочке лежит сто рублей. Но вот вопрос, куда поместить этот код? Раз тумбочка стоит в комнате, кажется логичным, что в локации комната нам и надо записать нашу заначку. На самом деле это не совсем так, но давайте попробуем сделать так. Вставьте код заначки в комнату.

Для игрока пока что ничего не изменилось – у него нет возможности увидеть значение переменных сами по себе. Но игра-то знает, что в заначке есть сто рублей. Как сделать их доступными для игрока? Например, создав подходящее действие! Пишите в локации «комната»:

ACT 'Заглянуть в тумбочку':
    ' В тумбочке лежит <<заначка>> руб.'
END

Теперь опробуйте, что получилось. Как видите вместо слова <<заначка>>, игра выдает цифру 100. Как это получается? Очень просто. Если вы заключите переменную в такие скобки << >>, то в тексте на этом месте отразится текущее значение переменной.

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

ACT 'Заглянуть в тумбочку':
    ' В тумбочке лежит <<заначка>> руб.'
    CLA
    IF заначка > 0:
        ACT 'Взять денег':
            a = $input('Сколько взять?')
            IF a > заначка:
                msg 'Первый закон подлости: нельзя взять из тумбочки больше денег, чем туда было положено!'
                GT $curloc
            ELSE
                деньги = деньги + a
                заначка = заначка - a
                GT $curloc
            END
        END
    END
    ACT 'Закрыть тумбочку':
        GT $curloc
    END
END

Ух как сложно все сразу стало! Ну ничего, сейчас разберемся. Давайте по порядку.

Первое неизвестное – оператор CLA. Тут все просто – это команда "очистить список действий". Как только мы выберем действие «Заглянуть в тумбочку», у нас пропадет само это действие и действие «выйти в коридор» - т.е. все, что имелось в списке действий. Между прочим, действия, если нужно, можно удалять и по одному. Для этого надо использовать команду такого типа: DELACT 'Заглянуть в тумбочку'. Но сейчас нам проще снести весь список действий целиком.

Дальше идет не совсем понятная строчка IF заначка > 0: — это команда с проверкой условия. Для проигрывателя она означает следующее: ЕСЛИ значение переменной заначка больше нуля, немедленно сделай все, что следует после двоеточия и вплоть до закрывающего оператора END. Как видите, это похоже на действие, только тут игроку не надо ничего выбирать. Программа сама проверяет условие и если условие верно, делает то что требуется.

Между IF и закрывающим его END идет действие «Взять денег». Таким образом, если заначка будет равна 0, то мы просто не увидим этого действия, т.к. условие не будет выполнено. И действительно, как можно взять деньги, которых нет?

Еще одна непонятная строчка a = $input('Сколько взять?') — тут дело обстоит следующим образом: a — это переменная. Точно такая же, как заначка. Но в данном случае, вместо того чтобы указать ее значение в коде, мы предоставляем выбор игроку. Оператор $input делает вот что: он выводит отдельное окошко, в котором пишет выражение, следующее за ним в кавычках. Кроме того, в окошке можно ввести текст. В данном случае – это должно быть число рублей, которые игрок хочет взять из тумбочки.

Дальше идет еще один IF, который проверяет, не захотел ли игрок захапать больше, чем есть. Если сумма, которую он пытается взять, больше, чем заначка, ему выводится предупреждение. А зачем там стоит оператор msg? Он позволяет вывести текст не в главном окне описаний, а в отдельном всплывающем окошке. Причем исполнение программы прервется до тех пор, пока игрок не нажмет на галочку, подтверждающую, что он прочел наше послание. Иногда это бывает удобно.

За посланием идет известное нам GT, но с каким-то непонятным $curloc да еще и без кавычек. Что это? Ведь мы не делали локации с таким названием? $curloc указывает на текущую локацию. В данном случае $curloc = 'Комната'. Мы могли бы написать и так и так – результат один и тот же. Просто я решил показать еще одну возможность и слегка сэкономить на печатных знаках.

И снова упираемся в неизвестность. else это такой интересный оператор, который используется вместе с IF. Все что идет ДО него, исполняется когда условие IF верно. Если же условие IF не верно, то выполняется то, что идет после else и до закрывающего его END. В данном случае, если игрок пытается взять из заначки не слишком много денег, то ему позволяется это сделать. Следующие две строки как раз это и обеспечивают. Строка деньги = деньги + a расшифровывается легко. Мы устанавливаем переменную деньги на значение равное нынешнему (имеющиеся деньги) + значение а (это количество денег, которые берет себе из заначки игрок).

При первом подходе к тумбочке деньги равны нулю, так что мы могли бы написать просто деньги = a. Но в таком варианте, когда игрок возьмет деньги второй раз, все, что у него было до этого, сгорит и значение денег снова придет к только что взятому количеству. Формула деньги = деньги + а позволяет нам не упустить те деньги которые уже есть у персонажа, когда бы он ни полез в тумбочку.

Аналогичная формула уменьшает значение денег оставшихся в заначке.

Запустите программу и попробуйте несколько раз взять деньги из тумбочки. Вы заметили? Что-то работает не правильно. Денег в заначке остается столько же, хотя мы честно прописали все условия. Неужели деньги не получается взять? А как узнать? Мы ведь нигде не видим, сколько денег есть в кармане.

Давайте проверим. Добавьте в локацию вот такой код:

PL 'В кармане: <<деньги>> руб.'

Оператор PL выведет наше выражение не в основном окне описаний, а в дополнительном. Так мы узнаем, сколько денег в кармане у героя. Попробуйте запустить программу снова и взять денег из тумбочки.

Сначала все выглядит нормально, но потом становится даже хуже, чем прежде. Строки с количеством денег в кармане громоздятся одна на другую, в кармане денег все прибывает, но в тумбочке по-прежнему сторублевая заначка. Не расстраивайтесь. Мы сейчас все поправим.

Для начала разберемся с дополнительным описанием. Дело в том, что при переходе на новую локацию через оператор GT у нас очищается основное окно описаний. Потому описания не громоздятся одно на другое, и даже фраза про деньги в тумбочке исчезает, если мы ее закрыли. Но дополнительное окно описаний само по себе не очищается. Иногда это удобно, но нам надо, чтобы там была всегда только одна строчка – та которая указывает на реальное состояние карманов героя.

Нам на помощь придет оператор CLR. Добавьте его перед PL, и все станет хорошо. CLR очищает окно дополнительного описания. Кстати, если вам понадобится вычистить основное окно, то можно использовать оператор *CLR. То же самое, но со звездочкой.

Однако это не решает проблемы бесконечных денег в тумбочке. Приятно конечно, но не совсем честно. Почему деньги прибавляются, но заначка не убавляется?

На самом-то деле убавляется: наш код заначка = заначка - a работает верно. Почему тогда денег остается 100 рублей? Тут надо понять как работает GT. Сразу после того как мы взяли денег из тумбочки, программа возвращает нас на ту же локацию и прогоняет весь имеющийся на ней код заново. А какая строка есть в этом коде? Верно: заначка = 100. Программа честно считает, что заначка должна снова равняться 100, и исполняет приказ. Уменьшенную только что сумму заначки она снова приравнивает к стартовому значению.

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

Теперь все должно работать правильно. Проверьте. И заодно насладитесь тем, как при достижении заначкой порога 0 рублей, у нас исчезает действие «взять деньги». А теперь задание на самостоятельную работу: используя полученные только что знания, добавьте в меню действие позволяющее перекладывать деньги обратно из кармана в заначку. Если урок усвоен, у вас все должно получиться.

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

У программистов бытует такая шуточная задачка: «Буратине дали три яблока. Одно яблоко Буратина съел. Сколько яблок осталось у Буратины?» Правильный ответ: «Хрен его знает, сколько яблок было у него до того, как ему дали еще три». Тут есть важная мораль, и звучит она так: обнуляйте переменные. Когда вам надо начинать с чистого листа, на помощь приходит замечательный оператор KILLALL. Он позволяет стереть значения всех имеющихся переменных и объекты из инвентаря. Давайте поставим такую строчку в начальной локации. Если нам потом захочется в конце нашей игры сделать вариант «начать сначала» и GT «Начало». Тогда у нашего героя не окажется в кармане миллиона, заработанного в предыдущей игре.

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

Правда такие переменные обозначаются немного иначе – перед ними стоит знак $. Мы можем сделать так, например: $name = 'Вася'. Но это не имеет особого смысла. Почему бы просто не писать в нужном месте «Вася», если уж мы решили что героя зовут именно так? Переменная тут не нужна.

Зато, благодаря текстовой переменной, мы можем предложить игроку самому назвать своего героя! Вы можете и сами догадаться, как это сделать, но я подскажу. Мы просто слегка изменим код действия «начать игру» в стартовой локации:

ACT 'Начать игру':
    $name = $input('Представьтесь, пожалуйста.')
    GT 'Комната'
END

Теперь игрок может ввести любое нравящееся ему имя, а мы можем использовать это имя, где захотим по ходу игры (при помощи конструкции <<$name>>).

Но что если игрок ошибется и оставит поле для имени пустым? Это не вызовет сбоя в программе, конечно, но там, где мы вставим в игру имя героя, будет красоваться пустое место. А это некрасиво. Давайте добавим проверочное условие.

ACT 'Начать игру':
    $name = $input('Представьтесь, пожалуйста.')
    IF $name = '':
        $name = 'Хакер'
    END
    GT 'Комната'
END

Т.е. если имя будет равно пустой строке, то игра будет величать героя прозвищем «Хакер». Внимательно следите за наличием $ в названии текстовой переменной. Если вы напишете name = 'Вася', то программа выдаст ошибку!

Ну вот, можно возвращаться снова к игре.

Пожалуй, стоит завести в квартире кота. Кот это хорошо. Мы, конечно, могли бы дать возможность игроку самому выбрать имя кота – мы это уже умеем. Но для целей этого руководство будет полезен другой подход. Давайте сделаем так, чтобы имя кота случайно выбиралось из трех вариантов. Например «Пушок», «Слоупок» и «Йцукен».

Тут то у нас и появится повод изучить сразу две важные вещи – работу рандомайзера и понятие о «массивах». Итак, массивы - что это такое? Фактически, мы уже с ними работаем. Каждая переменная является элементом массива. Массив – это множество переменных, с одним и тем же именем, но разными индексами. Поясню на примере.

Вот у нас есть три имени для кота. Мы забьем их в массив, вот таким образом:

$кот[1] = 'Пушок'
$кот[2] = 'Слоупок'
$кот[3] = 'Йцукен'

Как видите, мы имеем просто три текстовые переменные. Но на самом деле это три элемента единого массива $кот. Кстати, если мы напишем, например $кот = 'Слоупок', то это будет полностью эквивалентно по значению коду $кот[0] = 'Слоупок'. Просто ноль тут опускается за ненадобностью. Каждая переменная без численного индекса автоматом считается нулевой переменной одноименного массива.

Но зачем городить огород, спросите вы? А как обычно, для упрощения себе жизни. Массивы могут быть невероятно полезны в этом смысле. И вот вам живой пример. Как создать случайный шанс выпадения любого из трех имен котов в начале игры?

Для этого достаточно в начальной локации прописать такой код:

кличка = RAND (1,3)

Оператор RAND случайным образом выберет число от 1 до 3 – именно этот диапазон мы указали ему в скобках. Теперь в любом месте игры, для указания клички кота, мы можем написать конструкцию вида <<$кот[кличка]>>. И никакой возни с IF/else и так далее!

Давайте-ка изучим возможности инвентаря, используя нашего замечательного именного кота. Условия такие: кот программиста настолько ленив, что не собирается передвигаться сам. Он привык, чтобы его носили на руках. Реализуем кота, как объект, который изначально находится в коридоре, который можно таскать с собой и положить в любом другом месте. А потом обратно взять и снова таскать. И так пока не надоест.

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

Придется применить команду IF. Переменная «где_кот» будет задавать местонахождение кота. Если она равна 0 – значит кот на руках у хозяина. А для каждой комнаты мы определим свое число – если кот = числу комнаты, значит кот в этой комнате. Пусть коридору соответствует число 1.

Тогда в локации «Коридор» надо прописать:

IF кот = 1: ' На полу вальяжно развалился кот <<$кот[кличка]>>'

Заметьте, что тут мы используем не обычную конструкцию IF – все в одну строчку и без оператора END. Это упрощенный вариант, который мы можем использовать в тех случаях, когда требуется выполнение одного единственного условия. Кстати такой же вариант возможен и для ACT. Но это не обязательно:

IF кот = 1:
    ' На полу вальяжно развалился кот <<$кот[кличка]>>'
END

Вариант, приведенный выше, тоже сработает.

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

Кстати, что мы упускаем? Верно! Надо поставить в начальной локации условие кот = 1. Иначе кот не окажется в коридоре. Между прочим, используя уже полученные знания, вы можете написать условие, по которому кот будет стартовать в случайной локации. Считайте это домашним заданием.

Но как же взять его на ручки?

В каждой локации, где может оказаться кот, нам придется вставить подобный код:

IF кот = 1:
    ACT 'Взять кота за шкварник':
        кот = 0
        ADDOBJ 'Кот <<$кот[кличка]>>'
        DELACT 'Взять кота за шкварник'
    END
END

Естественно, номер локации должен совпадать со значением кота. Команда ADDOBJ – добавляет предмет, с соответствующим названием в инвентарь. Теперь мне достаточно сообщить вам, что для удаления из инвентаря используется команда DELOBJ, и вы сможете сами прописать вторую часть: возможность положить кота обратно.

Если все получилось, то вы просто обязаны наградить себя чем-нибудь вкусненьким. Коты знают толк в поощрениях! От себя добавлю, что для того чтобы класть кота можно было использовать не только условие вида IF кот = 0, но и условие IF OBJ 'Кот <<$кот[кличка]>>'. Практически любую задачу можно решить многими путями. Не приучайте себя к шаблонам – ищите оптимальный вариант для каждого случая. Это отличная зарядка для ума.

Итак, мы можем таскать с собой кота. А можем и не таскать. Но интерактивности что-то маловато. К счастью, этому горю несложно помочь. Время познакомиться с концепцией всплывающего меню и локаций-обработчиков.

Локация-обработчик — это вариант служебной локации. Т.е. такой, которая уж точно не является никаким «местом» в мире игры, а просто выполняет какую-то вспомогательную функцию. Существует много вариантов локаций-обработчиков, но мы начнем с «локации обработчика выбора предметов». Напишите в самом начале стартовой локации вот такой код:

$ONOBJSEL = 'выбор_объекта'

Это команда, которая означает следующее: как только игрок выделит мышкой любой объект в инвентаре, программа должна в фоновом режиме перейти к локации под названием «выбор_объекта». Что значит в фоновом режиме?

Как вы помните, команда GT стирает все, что написано в главном окне описания, а так же удаляет все действия, после чего переходит к выполнению кода новой локации. Но есть способы перейти к новой локации помягче. Если вместо GT вы напишете XGT, то описание, имевшееся в главном окне, не будет стерто при переходе на новую локацию. Есть и еще более мягкий способ, который я называю «фоновым». Он задается командой GS (от полной формы GOSUB). В этом случае, ничего не стирается и перехода на новую локацию фактически не происходит – оставаясь на прежней локации, программа одновременно выполняет действия указанные на локации, куда ее отправили. Выполнение кода текущей локации при этом не прерывается.

Т.е. если вы поставите какой-то код после GT, программа до него просто не доберется, перескочив на новую локацию. А вот GS можно ставить сколько угодно, программа выполнит то, что ей предписано там, и будет продолжать выполнять код текущей локации. Собственно вот такую форму перевода и использует оператор $ONOBJSEL, только не по команде игрока, а автоматически, каждый раз когда выделяется любой предмет в инвентаре.

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

IF $selobj = 'Кот <<$кот[кличка]>>':
    $кошак[0] = 'Осмотреть:осмотр_кота'
    $кошак[1] = 'Погладить:погладь_кота'
    $кошак[2] = 'Положить:полож_кота'
    menu '$кошак'
    unsel
END

Мы только что создали всплывающее меню для объекта. Что говорит плееру приведенный выше код? Он говорит, что если в данный момент выделен объект "кот", то надо сделать меню. Собственно само меню задается командой menu '$кошак', но само по себе оно будет пустым. Массив $кошак, идущий перед вызовом меню, – это список вариантов, которые данное меню будет предоставлять.

Как видите, элементам массива приписаны довольно странные выражения. Поясним. То что идет перед двоеточием, это название пункта меню, которое будет видно игроку. То что идет после двоеточия, это название локации, на которую надо будет перейти в фоновом режиме при выборе данного пункта меню. Так как локаций с этими названиями нет, то меню будет работать с ошибкой. Создайте локации под названиями: «осмотр_кота» , «погладь_кота» и «полож_кота». Теперь ошибки не будет – запустите программу и посмотрите, как это работает.

Естественно, при клике на пункты меню ничего не будет происходить. Все потому, что локации котоые мы создали, — пустые. Там нечего выполнять. Но вы можете это исправить сами. Просто напишите в этих локациях, что должно произойти в результате осмотра, поглаживания или водружения кота обратно на пол. Заметьте, теперь можно обойтись без специального действия в каждой локации. Кота можно положить обратно в любой локации, прямо через всплывающее меню!

Правда вот незадача… положить то кота мы можем. И даже можем заставить его исчезнуть из инвентаря. Но как программа узнает, ГДЕ мы его положили? Разочарую вас – сама по себе она этого делать не станет, и нам придется что-то придумать. Это не слишком сложно.

Давайте рассмотрим проблему. Когда мы кладем кота, то должны дать переменной «кот» значение отличное от нуля. Если мы пишем код в самой локации через ACT, то тут нет никакой проблемы – мы просто ставим цифру, которую назначили для этой локации. Но если кот кладется через меню, то это может происходить, где угодно. Т.е. нам надо решить ту же задачу, но для общего случая.

В каждой локации напишите подобный код: лока = 1. Естественно единичка — это будет для коридора. Для других комнат присвойте переменной «лока» другие значения-номера. Теперь локация «полож_кота» может выглядеть примерно так:

DELOBJ 'Кот <<$кот[кличка]>>'
кот = лока
GT $curloc

С тем, как работает этот код, вы легко разберетесь самостоятельно.

Ах, да. При рассмотрении меню мы упустили оператор unsel. Прямого отношения к меню он не имеет, а просто дает программе команду снять выделение с объекта. Если бы выделение не снималось, меню стало бы «одноразовым», и игроку самому приходилось бы придумывать как снять выделение с кота. Это можно сделать, выделив другой предмет, но у нас он пока только один. Так что не насилуйте мозги своих будущих игроков и не забывайте ставить оператор unsel на действия с объектами.

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

$ONNEWLOC = 'переход'

И соответственно создайте служебную локацию под названием «переход». На этой локации мы можем задавать события, которые будут происходить при посещении каждой новой локации. Давайте с помощью нее, мы реализуем для нашего героя возможность проголодаться.

Добавьте на локацию «переход» вот такой код:

CLR
голод = голод + 1
IF голод > 10:
    PL 'Вы проголодались!'
END

В результате, при каждом переходе между локациями (кроме фоновых переходов), к значению переменной голод будет добавляться единичка. И когда голод увеличится до 11+, в окне дополнительных описаний будет появляться фраза «Вы проголодались!». А CLR в самом начале, нужно нам для того, чтобы эта фраза не размножалась.

Только вот беда, это будет затирать наше сообщение о количестве денег в кармане! Но это не беда. Давайте перенесем код PL 'В кармане: <<деньги>> руб.' из локации «Комната» в локацию «переход». Лучше всего поставить ее после CLR и перед проверкой на голод. Тогда сначала будет отражаться количество денег, а потом (если проголодался) предупреждение о голоде.

Кстати отслеживать такие переменные, как количество денег, здоровье персонажа и т.п. лучше всего именно при каждом переходе на новую локацию. Это практичнее чем вставлять такой код на каждой локации, где значение может измениться – их ведь может быть очень много!

Ну а раз герой может проголодаться, значит ему нужно научиться и питаться. Добавить на кухне холодильник с продуктами, плиту и программу приготовления еды вы сможете сами. Дам только одну подсказку – когда герой поест, надо обнулить или снизить на какое-то значение переменную «голод». Впрочем, вы наверняка догадались об этом сами. Вообще, теперь у вас есть достаточно инструментов, которые позволят нарастить функционал игры, настолько, насколько позволит ваша фантазия.

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

Тем не менее, у нас остался еще один большой не разобранный кусок – красивости. QSP способно и на них, а оболочка AERO и вовсе затачивалась на мультимедийные эффекты. Конечно, это те эффекты, которых можно ожидать от текстовой игры, но в этих рамках они на высоте.

Итак, во-первых, QSP поддерживает html-теги. Т.е. фактически вы можете верстать игру как веб-страницу, используя блочную или табличную разметку, картинки, звуки, гиперссылки и т.п. Html поддерживается не полностью, но в достаточном объеме – полный список можно найти в файле справки по AERO Shell.

Применять html мы в рамках данного руководства вас учить не будем, остановимся только на основных, либо специфических моментах. По самому же языку разметки html в сети есть более чем достаточно руководств.

Для того, чтобы игра понимала html, необходимо в начальной локации поставить такую строчку: USEHTML = 1. Соответственно, если вы захотите в какой-то момент отключить поддержку html, то нужно будет написать USEHTML = 0.

Как вставлять html код? Он будет читаться прямо по ходу любых текстовых выражений. Например, мы можем слегка отредактировать заголовок игры, используя CSS стили:

'<div style="font-size: 30px; font-family:georgia; font-weight:bold; text-align:center; color:#ff0000">МОЯ ПЕРВАЯ ИГРА</div>'

Как видите, html теги используются здесь прямо в выражении (внутри кавычек). Т.е. надо понимать, что плеер воспринимает html не как собственный код QSP, а как разметку самого текста. Тем не менее, можно делать всякие хитрые вещи. Например, вставить в текст описания гипертекстовую ссылку, которая будет активировать действие.

Давайте предположим, что мы хотим сделать в коридоре криво висящую картину, которую можно будет поправить. Идем в локацию «Коридор» и вставляем туда такой текст описания:

IF картина = 0:
    ' На стене висит <a href="exec:GS ''поправь''">картина</a> в дешевой пластиковой раме. Картина слегка перекошена.'
else
    ' На стене идеально ровно висит картина в дешевой пластиковой раме.'
END

Собственно кусок exec:GS поправь в ссылке href, это команда отправиться в локацию «поправь». Создайте эту локацию с кодом:

картина = 1
msg 'Непорядок! Надо бы поправить.'
GT $curloc

Заметьте, что мы перешли на «поправь» мягко, через GS, так что команда $curloc будет относиться к локации «Комната».

При помощи html и CSS вы можете сделать со шрифтами и картинками очень многое. Язык QSP, тем не менее, имеет и собственные операторы для работы с мультимедиа. Давайте перейдем теперь к ним и настроим интерфейс игры так, как нам этого хочется.

Во-первых, было бы неплохо отключить строку ввода – мы ведь не используем ее в программе, так что она просто маячит без толку на виду.

Для этого можно в стартовой локации прописать условие: SHOWINPUT 0. Это скроет строку ввода. Но для первой локации было бы неплохо скрыть и другие вспомогательные поля – дополнительное описание и список предметов. Они ведь там не нужны, верно? Так что добавим еще:

SHOWSTAT 0
SHOWOBJS 0

Но тут возникает проблема. В первой то локации нам вспомогательные поля не нужны, а вот в остальных – очень даже. Но теперь, пока мы их не включим специально, эти поля не будут отражаться. Так что модифицируем немножко код действия «начало игры»:

ACT 'Начать игру':
    $name = $input ('Представьтесь пожалуйста.')
    IF $name = '':
        $name = 'Хакер'
    END
    SHOWSTAT 1
    GT 'Комната'
END

Так мы сможем включить окно дополнительных описаний при начале игры. А вот окно с предметами хорошо было бы включать только тогда, когда у нас появляются какие-либо предметы. Давайте тогда в локации «переход» добавим условие:

IF countobj > 0:
    SHOWOBJS 1
else
    SHOWOBJS 0
END

Функция countobj считает количество предметов в инвентаре. Соответственно, наш IF будет проверять, есть ли в инвентаре хотя бы один предмет и когда надо включать/выключать показ окошка инвентаря.

Теперь, если вы запустите и проверите наши изменения, то увидите странную вещь. С одной стороны, поля которые мы убрали, остаются пустыми, но разметка никуда не делась! Дело в том, что AERO Shell размечает экран при помощи фоновой картинки. Те линии, которые мы принимаем за разметку окошек, на самом деле просто статичная картинка и она не отражает никаких изменений. В AERO Shell, если вы хотите обозначить границы различных зон, вам придется делать это самостоятельно. Размеры и координаты каждого поля так же придется устанавливать в коде.

Давайте этим займемся. Экран игры по умолчанию имеет размер 800х600 пикселей. Причем, хотя вы можете увеличивать или уменьшать размер окна, оно будет всеми силами сохранять именно эту пропорцию. Так что мы можем воспринимать размер 800х600 как абсолютный. При желании вы можете создать новую подложку для окна игры, с разметкой по зонам или без оной, целиком. Для этого найдите или создайте желаемую фоновую картинку соответствующего размера и назовите ее, например, фон.jpg (для теста вполне подойдет какой-нибудь wallpaper). Плеер поддерживает и другие распространенные графические форматы, так что вы можете использовать к примеру .gif (даже анимированный) либо, как это рекомендуется разработчиком, .png с поддержкой прозрачности слоев.

Создайте непосредственно в директории с файлом игры папку «content» и положите фоновый рисунок туда. Осталось добавить на стартовую локацию код:

$BACKIMAGE = 'content/фон.jpg'

Запустите программу и посмотрите что получилось.

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

Назовем наши файлы «беж.jpg», «кор.jpg» и «жел.jpg» и сложим их в папку «content». Теперь можно стереть строчку BACKIMAGE и добавить на стартовую локацию код:

$MAINDESC_BACKIMAGE = 'content/жел.jpg'
$STATDESC_BACKIMAGE = 'content/жел.jpg'
$ACTIONS_BACKIMAGE = 'content/кор.jpg'
$OBJECTS_BACKIMAGE = 'content/беж.jpg'

Он замостит соответствующие поля созданными нами квадратиками. Точно так же вы можете поместить туда любое другое изображение. Благодаря цветной маркировке мы сможем видеть какое место реально занимает каждое из наших полей.

Если вы запустите программу, то увидите, что отключенные поля просто отсутствуют (на их месте серый фон), а включенные лежат ровно в тех рамках, которые установлены программой по умолчанию. Нас это не слишком устраивает, так что давайте поиграемся с размерами полей.

Начнем со стартовой локации, где у нас всего два поля: основное и список действий. Нужно чтобы они занимали все свободное пространство. Как сказано выше, размер окна игры составляет 800х600 пикселей. Исходя из этого, нам надо задать ширину главного поля, а так же ширину и высоту списка действий:

MAINDESC_W = 800
ACTIONS_H = 200
ACTIONS_W = 800

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

ACT 'Начать игру':
    
! $name = input 'Представьтесь пожалуйста.'
    IF $name = '':
        $name = 'Хакер'
    END
    SHOWSTAT 1
    ACTIONS_W = 590
    GT 'Комната'
END

И так (в обработчике перехода на новую локацию):

IF countobj > 0:
    SHOWOBJS 1
    MAINDESC_W = 590
else
    SHOWOBJS 0
    MAINDESC_W = 800
END

Теперь размеры всех полей будут динамически изменяться и заполнять весь экран игры.

Вы можете задавать подложки и для других полей, например для окошек msg, input или menu – соответствующие команды можно найти в справке по AERO Shell.

А что если нам хочется добавить картинки посерьезнее? Например, чтобы вместо описания комнаты, выводилась ее фотография? Попробуйте найти подходящую картинку или просто сфотографируйте свою комнату на цифровой аппарат. Поместите файл с изображением комнаты в папку «content».

Теперь, нам надо решить, куда поместить изображение комнаты. Можно конечно сделать так:

$MAINDESC_BACKIMAGE = 'content/комната.jpg'

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

Более изящным решением будет оформить картинку при помощи CSS и html. Вы можете даже создать свои HTML или CSS шаблоны для всех полей и окон плеера, при помощи таких операторов серии $xxxxxx_FORMAT (вы найдете их список в справке). Вот например простейший html шаблон-таблица для основного окна описаний:

$MAIN_FORMAT='<table><tr><td>%TEXT%</td></tr></table>'

Вместо %TEXT% программа будет подставлять в шаблон тот текст, который задан в каждой локации для основного окна описаний. Сам шаблон написан на html, но о том, как это правильно сделать, вам лучше расскажут профильные руководства.

Есть правда у QSP еще несколько собственных операторов для работы с изображениями, которые могут оказаться полезны. Если вы хотите сделать более красивые переходы между локациями, вам поможет инструмент эффектов перехода. Добавьте на стартовую локацию строчку:

$NEWLOC_EFFECT = 'fade'

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

quake
blur
h_blinds
v_blinds
l_slide
r_slide
u_slide
d_slide
iris
photo
pixels
rotate
v_squeeze
h_squeeze
zoom
wipe1
wipe2
wipe3
wipe4

Если вы хотите чтобы эффект происходил быстрее или медленнее, то его скорость можно настроить оператором NEWLOC_EFFECT_TIME. Например, такая вот строчка растянет время эффекта перехода до 2-х секунд:

NEWLOC_EFFECT_TIME = 2000

Вы можете сменить эффект перехода на новый в любом месте кода, просто добавив новый $NEWLOC_EFFECT. Он сменит собой ранее установленный эффект перехода. А такая строчка отключит эффекты перехода вообще:

$NEWLOC_EFFECT = ''

Вы можете добавлять перечисленные эффекты не только к основному окну игры но и ко всплывающим окошкам, таким как msg, input, menu и view. Операторы, нужные для этого перечислены в справке. Принцип их работы полностью аналогичен описанному выше.

А вот еще одна опция, пожалуй, даже более нужная. Предположим, что вы хотите, чтобы при открытии меню объекта «кот» нам демонстрировалась картинка кота. Для начала надо будет найти подходящую картинку, конечно. Сохраните ее в папку «content».

Теперь можно слегка изменить код в локации обработчике выбора предметов:

IF $selobj = 'Кот <<$кот[кличка]>>':
    view 'content/кот.jpg'
    $кошак[0] = 'Осмотреть:осмотр_кота'
    $кошак[1] = 'Погладить:погладь_кота'
    $кошак[2] = 'Положить:полож_кота'
    menu '$кошак'
    unsel
END

Оператор view демонстрирует картинку, поверх всего остального, прямо по центру. Если кликнуть по картинке, она исчезнет. Но, на случай если игрок об этом не догадается, можно добавить в нужном месте другой оператор view, без аргументов. Это закроет демонстрируемую картинку автоматически. Вы так же можете задать местоположение и размеры картинки через операторы: VIEW_X (координата Х), VIEW_Y (координата Y), VIEW_W (ширина) и VIEW_H (высота).

Домашнее задание: сделайте так, чтобы изображение кота демонстрировалось не в момент открытия меню, а при нажатии пункта «осмотреть». Выберите более подходящее место и размер для изображения.

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

Если у вас вдруг завалялся звуковой файл с кошачьим мяуканьем, поместите его в папку «content» и сделайте так:

IF $selobj = 'Кот <<$кот[кличка]>>':
    play 'content/кот.mp3'
    $кошак[0] = 'Осмотреть:осмотр_кота'
    $кошак[1] = 'Погладить:погладь_кота'
    $кошак[2] = 'Положить:полож_кота'
    menu '$кошак'
    unsel
END

Тогда, при открытии кошачьего меню раздастся мяуканье.

Вы можете использовать оператор play любым удобным образом, например, чтобы создать плей-лист с фоновой музыкой.

Оператор play поддерживает изменение громкости проигрывания в % отношении. Например, если вы хотите, чтобы кот в приведенном выше примере мяукал вполовину тише, то можно это записать так:

play 'content/кот.mp3',50

Здесь 50 – это 50% громкости.

Несколько звуковых файлов (а точнее до 32х) могут проигрываться одновременно. Если вам нужно выключить один из них, то тут подойдет оператор close. Например так:

close 'content/кот.mp3'

Остановить все проигрываемые звуковые файлы еще проще – для этого достаточно написать CLOSE ALL.

Объема операторов и приемов программирования, которые мы освоили, вполне должно хватить для написания собственной игры. Не бойтесь комбинировать, экспериментировать и находить неординарные решения. С вопросами, которые не получится решить самостоятельно, смело обращайтесь в сообщество QSP на официальном форуме или канале в дискорде.

Удачи!

Автор: MASTERSET

23.04.2010 00:27

Мелкие правки: Aleks Versus

19.07.2022 00:13