Программирование стратегических игр с DirectX 9.0

bda5893f

Функция CUnitManager::iLoadBaseTypes()


Вы узнали, качие элементы класса хранят информацию базовых типов, но как загружаются данные? Здесь вступает в игру функция iLoadBaseTypes(). Она получает пять параметров, каждый из которых является именем файла, содержащего импортируемые данные. Отдельные файлы требуются для данных защиты, данных атаки, данных передвижения, данных анимации и данных подразделений. Функция загрузки базовых типов получает имена пяти файлов и импортирует данные из них в диспетчер подразделений. На рис. 8.23 показана взаимосвязь между классом диспетчера подразделений и импортируемыми файлами.


Рис. 8.23. Импорт данных из пяти различных файлов в базовые типы CUnitManager

На рис. 8.23 показано как диспетчер подразделений загружает информацию в базовые типы из пяти различных файлов данных. Имена этих файлов BaseType_Defense.csv, BaseType_Offense.csv, BaseType_Movement.csv, BaseType_Unit.csv и BaseType_Animation.csv. Расширение имени файла .csv обозначает, что это файлы в формате с разделенными запятыми значениями. Такие файлы содержат значения, разделенные запятыми. Это общепринятый формат, поддерживаемый электронными таблицами, поскольку он позволяет сохранять данные в простом для импортирования формате. Лично я для ввода и редактирования информации о подразделениях использую программу работы с электронными таблицами Excel. Вот пример данных для базовых типов защиты:

Medium Heli Armor, 20, 2, 2, 30, 30, 0

Heavy Heli Armor, 30, 2, 2, 50, 100, 0

Light Heli Armor, 10, 2, 2, 20, 70, 0

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

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



Рис. 8.24. Данные защиты в электронной таблице Excel

На рис. 8. 24 показаны уже представленные ранее данные, но в виде гораздо лучше выглядящей электронной таблицы с названиями столбцов. Если у вас есть программа для работы с электронными таблицами или базами данных, экспорт в формат CSV осуществляется очень легко. Загляните в папку проекта D3DFrame_UnitTemplate, находящуюся среди сопроводительных файлов на CD-ROM и вы найдете там папку UnitData, содержащую csv-файлы с информацией о подразделениях, необходимой для данного примера.

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

// Открываем файл с данными базового типа fp = fopen(szDefFileName, "r"); if(fp == NULL) { return(-1); } // Читаем строку с заголовками столбцов и игнорируем ее fgets(szTempBuffer, 512, fp); szTempBuffer[strlen(szTempBuffer) - 1] = '\0'; // Устанавливаем общее количество объектов равным 0 m_iTotalDefObjs = 0;

После того, как файл открыт, я считываю первую строку текста. Она содержит названия столбцов, так что после чтения эти данные игнорируются. Затем количество объектов защиты устанавливается равным 0. После завершения описанных действий я последовательно считываю каждую строку файла, анализирую ее и инициализирую полученными данными очередной тип защиты. Вот код, выполняющий эти действия:

// Последовательный перебор строк файла while(!feof(fp)) { // Получаем следующую строку fgets(szTempBuffer, 512, fp); if(feof(fp)) { break; } // Добавляем разделитель szTempBuffer[strlen(szTempBuffer)-1] = '\0'; iStart = 0; iEnd = 0; iCurPos = 0; iCurValue = 0; // Извлекаем значение while(szTempBuffer[iCurPos] != '\0' && iCurPos < 512) { // Проверяем достигли ли конца значения if(szTempBuffer[iCurPos] == ',') { iEnd = iCurPos; memset(&szValue[iCurValue][0], 0x00, 32); memcpy(&szValue[iCurValue], &szTempBuffer[iStart], iEnd - iStart); iStart = iEnd + 1; iCurValue++; } iCurPos++; }; // Импорт последнего столбца iEnd = iCurPos; memset(&szValue[iCurValue][0], 0x00, 32); memcpy(&szValue[iCurValue], &szTempBuffer[iStart], iEnd - iStart); iStart = iEnd + 1; iCurValue++; ...



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

// Идентификатор типа m_DefenseObjs[m_iTotalDefObjs].m_iType = m_iTotalDefObjs; // Название strcpy(m_DefenseObjs[m_iTotalDefObjs].m_szName, &szValue[0][0]); // Коэффициент защиты от пуль m_DefenseObjs[m_iTotalDefObjs].m_iBulletArmorRating = atoi(&szValue[1][0]); // Коэффициент защиты от ракет m_DefenseObjs[m_iTotalDefObjs].m_iMissileArmorRating = atoi(&szValue[2][0]); // Коэффициент защиты от лазера m_DefenseObjs[m_iTotalDefObjs].m_iLaserArmorRating = atoi(&szValue[3][0]); // Коэффициент защиты в рукопашной m_DefenseObjs[m_iTotalDefObjs].m_iMeleeArmorRating = atoi(&szValue[4][0]); // Очки повреждений m_DefenseObjs[m_iTotalDefObjs].m_iMeleeArmorRating = atoi(&szValue[5][0]); // Скорость восстановления m_DefenseObjs[m_iTotalDefObjs].m_iMeleeArmorRating = atoi(&szValue[6][0]); // Увеличиваем количество объектов m_iTotalDefObjs++; } fclose(fp);

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

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

// Тип идентификатора m_AnimationObjs[m_iTotalAnimationObjs].m_iType = m_iTotalAnimationObjs; // Имя memset(m_AnimationObjs[m_iTotalAnimationObjs].m_szName, 0x00, 64); strcpy(m_AnimationObjs[m_iTotalAnimationObjs].m_szName, &szValue[0][0]); // Префикс memset(m_AnimationObjs[m_iTotalAnimationObjs].m_szBitmapPrefix, 0x00, 64); strcpy(m_AnimationObjs[m_iTotalAnimationObjs].m_szBitmapPrefix, &szValue[1][0]); // Количество кадров ожидания m_AnimationObjs[m_iTotalAnimationObjs].m_iNumStillFrames = atoi(&szValue[2][0]); // Количество кадров перемещения m_AnimationObjs[m_iTotalAnimationObjs].m_iNumMoveFrames = atoi(&szValue[3][0]); // Количество кадров атаки m_AnimationObjs[m_iTotalAnimationObjs].m_iNumAttackFrames = atoi(&szValue[4][0]); // Количество кадров гибели m_AnimationObjs[m_iTotalAnimationObjs].m_iNumDieFrames = atoi(&szValue[5][0]); // Установка устройства визуализации m_AnimationObjs[m_iTotalAnimationObjs].vSetRenderDevice(m_pd3dDevice); // Загрузка текстур m_AnimationObjs[m_iTotalAnimationObjs].vLoadTextures(); // Увеличение количества объектов m_iTotalAnimationObjs++;



Приведенный выше код похож на остальные фрагменты кода за исключением вызовов двух методов объекта анимации. Первый из них, vSetRenderDevice(), устанавливает внутренний указатель объекта анимации на устройство визуализации Direct3D. Это позволяет объекту загружать текстуры. Второй метод, vLoadTextures(), использует информацию, хранящуюся в csv-файле данных анимации для загрузки необходимых для анимации текстур. Он формирует имена файлов, комбинируя заданный в данных анимации префикс растровой графики со значением счетчика кадров. На рис. 8.25 показаны данные для типов атаки.



Рис. 8.25. Данные атаки хранящиеся в электронной таблице Excel

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

// Тип защиты ptrDefense = ptrGetDefenseType(&szValue[1][0]); // Первый тип атаки ptrOffense1 = ptrGetOffenseType(&szValue[2][0]); // Второй тип атаки ptrOffense2 = ptrGetOffenseType(&szValue[3][0]); // Третий тип атаки ptrOffense3 = ptrGetOffenseType(&szValue[4][0]); // Тип передвижения ptrMovement = ptrGetMoveType(&szValue[5][0]); // Тип анимации ptrAnimation = ptrGetAnimType(&szValue[6][0]); // Установка базовых типов m_UnitBaseObjs[m_iTotalUnitBaseObjs].vSetBaseValues( ptrDefense, ptrOffense1, ptrOffense2, ptrOffense3, ptrMovement, ptrAnimation);

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



Рис. 8.26. Данные подразделений хранящиеся в электронной таблице Excel


Содержание раздела