| netlib.narod.ru | < Назад | Оглавление | Далее > |
Уф! Могу честно сказать, что самая сложная часть позади, поскольку реализовать сейчас выполнение скриптов — это просто детские игрушки. Вы можете выбросить за дверь шаблоны действий, потому что теперь будете работать только со структурами sScript и sScriptEntry.
Первый этап работы со скриптом — загрузка его в память, выполняемая с помощью функции LoadScript (если вы хотите больше узнать об этой функции, обратитесь к разделу «Создание элементов скрипта»).
long NumActions;
sScript *LoadedScript = LoadScript("Script.mls", &NumActions);
После этого ваш игровой движок просто перебирает элементы связанного списка скрипта для выполнения каждого действия. Это требует некоторого объема кодирования, поскольку в этом случае действия известны только по номерам (так что вы должны знать, что каждое из действий делает). Вот пример, перебирающий элементы загруженного скрипта и ищущий действие Print (действие 0), содержащее единственный элемент (текст для печати):
sScript *ScriptPtr = LoadedScript; // Начинаем с корня
// Перебираем в цикле все действия скрипта
while(ScriptPtr != NULL) {
// Это действие 0?
if(ScriptPtr->Type == 0) {
// У данного действия один элемент - текст.
// Отображаем текст в окне сообщений
MessageBox(NULL, ScriptPtr->Entries[0].Text, "TEXT", MB_OK);
}
// Переходим к следующему действию в скрипте
ScriptPtr = ScriptPtr->Next;
}
Хотя здесь всего несколько строк кода, они демонстрируют удивительный потенциал обработки скриптов. Немного изобретательности и вы сможете приспособить MLS для обработки большинства скриптовых действий.
Как насчет использования условных инструкций if...then...else? Как вы знаете, эта инструкция проверяет, равно значение условия true или false, и, в зависимости от результата, выполняет различные последовательности действий. Возьмем, к примеру, следующий код на С:
BOOL GameFlags[256]; // Флаги, используемые в игре
if(GameFlags[0] == TRUE) {
// Печатаем сообщение и устанавливаем флаг в FALSE
MessageBox(NULL, "It's TRUE!", "Message", MB_OK);
GameFlags[0] = FALSE;
} else {
// Печатаем сообщение
MessageBox(NULL, "It's FALSE.", "Message", MB_OK);
}
В зависимости от значений, хранящихся в массиве GameFlags, выполняются различные блоки кода. Создав несколько действий и слегка переработав код обработки скрипта, вы можете наслаждаться преимуществами использования конструкции if...then...else в MLS. Сперва взгляните на шаблон действий:
"If GameFlag ~ equals ~ then" INT 0 255 BOOL "Else" "EndIf" "Set GameFlag ~ to ~" INT 0 255 BOOL "Print ~" TEXT
Здесь нет ничего особенного, поскольку настоящая работа происходит в коде выполнения скрипта:
// pScript = загруженный скрипт, содержащий следующий код:
// "If GameFlag (0) equals (TRUE) then"
// "Print (It's TRUE!)"
// "Set GameFlag (0) to (FALSE)"
// "Else"
// "Print (It's FALSE.)"
// "EndIf"
// Функции обработки действий
sScript *Script_IfThen(sScript *Script);
sScript *Script_Else(sScript *Script);
sScript *Script_EndIf(sScript *Script);
sScript *Script_SetFlag(sScript *Script);
sScript *Script_Print(sScript *Script);
// Структура исполнения действия скрипта
typedef struct sScriptProcesses {
sScript *(*Func)(sScript *ScriptPtr);
} sScriptProcesses;
// Список структур исполнения действий скрипта
sScriptProcesses ScriptProcesses[] = {
{ Script_IfThen },
{ Script_Else },
{ Script_EndIf },
{ Script_SetFlag },
{ Script_Print }
}
BOOL GameFlags[256]; // Массив игровых флагов
void RunScript(sScript *pScript)
{
// Очищаем массив GameFlags,
// устанавливая везде значение FALSE
for(short i = 0; i < 256; i++)
GameFlags[i] = FALSE;
// Сканируем скрипт и выполняем функции
while(pScript != NULL) {
// Вызываем функцию скрипта и прерываем обработку,
// если она вернула NULL. Любое другое
// возвращенное значение - это указатель на следующую
// функцию, обычно pScript->Next.
pScript = ScriptProcesses[pScript->Type].Func(pScript);
}
}
sScript *Script_IfThen(sScript *Script)
{
BOOL Skipping; // Флаг пропуска действий скрипта
// Смотрим, совпадает ли флаг со вторым элементом
if(g_Flags[Script->Entries[0].lValue % 256] ==
Script->Entries[1].bValue)
Skipping = FALSE;
else
Skipping = TRUE;
// Здесь флаг Skipping установлен, если действия скрипта должны быть
// пропущены согласно условной инструкции if ... then. Действия выполняются,
// если Skipping = FALSE, также ищется else для переключения режима пропуска
// или endif для завершения условного блока
// Переходим к обработке следующего действия
Script = Script->Next;
while(Script != NULL) {
// Если Else, переключаем режим пропуска
if(Script->Type == 1)
Skipping = (Skipping == TRUE) ? FALSE : TRUE;
// Прерывание на EndIf
if(Script->Type == 2)
return Script->Next;
// Обрабатываем функции скрипта в условном блоке,
// при этом действия пропускаются, если условие не выполнено
if(Skipping == TRUE)
Script = Script->Next;
else {
if((Script =
ScriptProcesses[Script->Type].Func(Script)) == NULL)
return NULL;
}
}
return NULL; // Достигнут конец скрипта
}
sScript *Script_SetFlag(sScript *Script)
{
// Установка логического флага
GameFlags[Script->Entries[0].lValue % 256] =
Script->Entries[1].bValue;
}
sScript *Script_Else(sScript *Script) { return Script->Next; }
sScript *Script_EndIf(sScript *Script) { return Script->Next; }
sScript *Script_Print(sScript *Script)
{
MessageBox(NULL, Script->Entries[0].Text, "Text", MB_OK);
return Script->Next;
}
Как видите, все волшебство сосредоточено в рекурсивной функции Script_IfThen, обрабатывающей действия скрипта, находящиеся между парой if...then и действием EndIf. Действие Else просто переключает режим обработки (с пропуска действий на их выполнение или наоборот) в зависимости от исходного значения переменной Skipped.
Теперь вы почувствовали мощь, а если вам нужны еще доказательства, обратитесь к последующим главам, где используется система MLS, например, к главе 12, «Управление игроками и персонажами», и главе 16, «Объединяем все в законченную игру». Обе главы демонстрируют использование скриптов при взаимодействии с игровыми персонажами.
| netlib.narod.ru | < Назад | Оглавление | Далее > |