22 февраля 2011

Скриптовой модуль, Демо-5

Вот и пришла пора выполнить свое обещание, данное когда-то. Итак, в этом сообщении я опишу работу 5ой демки к скриптовому модулю, что выкладывал на всеобщее обозрение. Так как я близок к теме создания игровых программ, то и здесь я не смог удержаться, чтобы не показать простой способ использования скриптов для построения диалогов, что может быть применимо в компьютерных играх и интерактивных системах. С одной стороны - скрипты как нельзя лучше подходят для этой задачи, как минимум по трем причинам:

  • результат виден сразу, никакой перекомпиляции!
  • тексты диалогов лежат в отдельных файлах скриптов, не нужно производить навигацию по десятку разрозненных pas-файлов
  • можно отдать файл на редактирование постороннему человеку и быть уверенным, что никаких небезопасных действий с программой он не учудит (ему даже исходных кодов основной игры можно не давать)

С другой стороны, приближение демок к реальным примерам использования, должно только положительно сказываться на их восприятии. Ну кому интересно смотреть демки с квадратом, двигающимся по окружности? В общем, сегодня будем "лепить" маленький диалог на скриптах...
Не знаю как для остальных, а для меня уже этих трех вышеупомянутых факторов достаточно для того, чтобы перенести диалоговую часть игры в скрипты!
Итак, что нам нужно?
Уметь задавать вопрос и предлагать несколько вариантов ответа на него.
Всегда, прежде чем программировать диалоги, цепочку их последовательности рисуют на бумаге. Так проще, ведь держать в голове десяток вопросов и их взаимосвязи довольно трудная задача, да и в дальнейшем проследить правильность выполнения программы намного проще, когда все наглядно перед глазами.
Предлагаю схему вопросов, которые задаются в демке:
А вот и скрин демки, на котором можно увидеть способ выбора ответа - это обычный RadioGroup с кнопкой "Answer it!!!", которая передаст выбранное значение в скрипт:


А теперь приступим к реализации...
Зарегистрируем для скриптов две функции:

Set_QuestionText('Итак, ты уже изучил Lua?');
и
Add_Answer(2, 'Конечно!');

Первая - задает вопрос, устанавливает текст вопроса, очищает список ответов.
Вторая - добавляет новый ответ в список, задает текст ответа и индекс вопроса, на который перейдет диалог при выборе этого ответа. В данном примере при выборе ответа "Конечно!" диалог перейдет на вопрос с индексом 2.

Теперь посмотрим, что происходит на стороне хост-программы.
При вызове Set_QuestionText() как я и говорил происходит очищение списка ответов и задание строки-вопроса:

Function Set_QuestionText(aLua_VM: Plua_State): integer; cdecl;
begin
  frmMain.ClearAnswers;
  frmMain.lblQuestion.Caption := Lua_ToString(aLua_VM, 1);
end;

При вызове Add_Answer()
а) добавляется новый индекс ответа в массив fAnswersID[], 
б) добавляется элемент в RadioGroup
код парсинга:


Function  Add_Answer(aLua_VM: Plua_State): integer; cdecl;
begin
  frmMain.fAnswersID[frmMain.rgAnswers.Items.Count] := Round(Lua_ToNumber(aLua_VM, 1));
  frmMain.rgAnswers.Items.Add(Lua_ToString(aLua_VM, 2));
  frmMain.rgAnswers.ItemIndex := 0;
  result := 0;
end;

Остается один вопрос - каким образом передать выбранное значение номера ответа и, следовательно, следующего вопроса в скрипт? До сих пор мы ни разу не воспользовались вторым параметром, передаваемом в

TScriptManager.DoScript(const aFunctionName: String; aArguments: Array of lua_Number; const aWriteError: boolean = true)

Для чего этот параметр нужен? Ответ прост - массив Single-значений, поданных в качестве второго аргумента пересылаются как параметры в скриптовую функцию.
Например, мы можем написать в хост-программе:

ScriptManager.DoScript('MyScript', [MyArgument]);

и теперь описание скриптовой функции может иметь уже входной параметр:

function MyScript(MyArgument)
...
end;

Я уже говорил, что подается целый массив параметров? Ну так вот, допустима и такая запись, когда мы через запятую формируем массив из нескольких параметров:

ScriptManager.DoScript('MyScript', [Argument1, Argument2, Argument3]);

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

function MyScript(Argument1, Argument2, Argument3)
...
end;

Здесь стоит уточнить один момент. Дело в том, что я, конечно, немного урезал функционал скриптов, разрешив передачу только Single-параметров. Но лично у меня не возникало необходимости в передаче аргументов других типов (строковых, например). Дело в том, что в Delphi мне показался самым красивым и простым способом передачи параметров - через массив. А вот хранение нетипизированных переменных в массиве в мире Delphi запрещено. Можно использовать тип Varinat, но он слишком медленный, чтобы применять его здесь. Именно поэтому я остановился на Single. Вот так, с этим вроде все.
Ну вот, по поводу Single-параметров вроде отговорился, теперь можно двигаться дальше.
Итак, в демке мы вызываем скриптовую функцию:

fScriptManager.DoScript('main', [fQuestIndex], false);

ну и приведу сам скрипт, ибо исходники мало кто качает, наверно:

function main(QuestIndex)
    if QuestIndex == 1 then
        Set_QuestionText('Итак, ты уже изучил Lua?');
        Add_Answer(2, 'Конечно!');
        Add_Answer(3, 'Не-а');
        Add_Answer(4, 'Вообще ничего не ясно.');
    elseif QuestIndex == 2 then
        Set_QuestionText('Молодец! Надеюсь, эти демо тебе помогли...');
        Add_Answer(5, 'Да, всё круто!');
        Add_Answer(4, 'Ерунда всё это');
    elseif QuestIndex == 3 then
        Set_QuestionText('Жаль, недеюсь, всё еще получится.');
        Add_Answer(4, 'Я тоже надеюсь');
        Add_Answer(5, 'Да я не переживаю, и без Lua справлюсь');
    elseif QuestIndex == 4 then
        Set_QuestionText('Перечитай всё заново - может станет яснее?');
        Add_Answer(1, 'Ок, попробую');
    elseif QuestIndex == 5 then
        Set_QuestionText('Я рад, удачи!');
        Add_Answer(1, 'Ага, и тебе...');
    end;
end;

Все новое разобрали, почти обо всем рассказано. Теперь стало ясно, как обработать нажатие кнопки "Answer it!!!", мы просто считываем выставленный ответ и выполняем скрипт:

procedure TfrmMain.btnAnswerClick(Sender: TObject);
begin
  fQuestIndex := fAnswersID[rgAnswers.ItemIndex];
  DoScript;
end;

Вот и все... Качаем, компилируем, отвечаем на вопросы программы, разговариваем по заскриптованныму диалогу.
Ура! Я дописал это чтиво :)

p.s. Кстати, а кто-нибудь заметил, что пропущена Демо-4? Я уже отвечал, что на самом деле я ее даже начал делать, но не придумалось ничего интересного, что можно было бы продемонстрировать. Вроде все обыденно-безынтересно. Так что, если будут замечательные идеи по поводу темы для 4ой программы - с радостью постараюсь реализовать и выложить.


Сообщения, схожие по тематике:

4 коммент.:

  1. Хорошие примеры.
    А вот нумеровать шкуру неубитого медведа чревато :)

    ОтветитьУдалить
  2. благодарю!
    по поводу нумерации - мне было проще сначала создать папки, проекты для демо, а потом уже по наличию времени каждую из них наполнять кодом, функционалом... как силы иссякли - решил выложить!))
    согласен, получилось чуднО, отдам этот первый блин комам;))

    ОтветитьУдалить
  3. Прошу прощения, но указанный в записке файл(http://lampogolovii.fileave.com/Demos/Demos_v1.rar)
    уже недоступен.
    Каким образом можно ознакомиться с его содержимым?

    ОтветитьУдалить
    Ответы
    1. боюсь, что теперь мне будет трудно найти демку...
      сервис fileave накрылся, теперь все демки, которые я хранил там, канули в лету...
      если найду на диске бэкап - обязательно выложу!

      Удалить