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 | < Назад | Оглавление | Далее > |