Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming / GameProg / RPG_Programming_2ed.pdf
Скачиваний:
240
Добавлен:
12.02.2016
Размер:
12.06 Mб
Скачать

Джим Адамс

объекта в мире, ICS может работать с несколькими экземплярами одного и того же объекта. Каждый раз, когда системе управления имуществом требуется информация о предмете, она обращается к главному списку предметов за его спецификациями. Это позволяет экономить память, сохраняя в ICS только ссылки с номерами объектов в MIL (как показано на рис. 11.7).

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

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

Разработка ICS карты

ICS карты следит за предметами, находящимися внутри уровней, включая те предметы, которые находятся внутри других, — например, меч лежащий в сундуке. Способ размещения предметов на карте зависит от используемого типа карты. На трехмерных картах для размещения предметов вы используете три координаты — X, Y и Z. Если вы разделили ваш мир на несколько карт, придется следить за предметами каждой карты, используя собственный файл предметов для каждой карты.

ICS карты представлена структурой и классом:

netlib.narod.ru

495

Глава 11. Определение и использование объектов

typedef struct sMapItem

 

{

// Номер предмета в MIL

long ItemNum;

long Quantity;

// Количество предметов

float XPos, YPos, ZPos;

// (например, монет)

// Координаты на карте

sMapItem *Prev, *Next;

// Указатели связанного списка

long Index;

// Индекс данного предмета

long Owner;

// Индекс владельца

sMapItem *Parent;

// Родитель предмета

sMapItem()

{

Prev = Next = Parent = NULL; Index = 0; Owner = -1;

}

~sMapItem() { delete Next; Next = NULL; } } sMapItem;

class cMapICS

{

private:

long m_NumItems; // Количество предметов на карте sMapItem *m_ItemParent; // Родитель связанного списка

//предметов карты

//Чтение из файла длинного целого числа

//или числа с плавающей точкой

long GetNextLong(FILE *fp); float GetNextFloat(FILE *fp);

public:

cMapICS(); // Конструктор ~cMapICS(); // Деструктор

//Загрузка, сохранение и освобождение

//списка предметов карты

BOOL Load(char *Filename);

BOOL Save(char *Filename);

BOOL Free();

//Добавление и удаление предмета на карте

BOOL Add(long ItemNum, long Quantity,

float XPos, float YPos, float ZPos, sMapItem *OwnerItem = NULL);

BOOL Remove(sMapItem *Item);

//Возвращает количество предметов или

//родителя связанного списка объектов

long GetNumItems(); sMapItem *GetParentItem(); sMapItem *GetItem(long Num);

};

Сначала вы видите структуру sMapItem, которая хранит информацию для каждого предмета на карте. ItemNum — это номер для ссылки на

элемент MIL (который должен находиться в диапазоне от 0 до 1023, если вы

496

netlib.narod.ru

Джим Адамс

используете программу MIL Editor), а Quantity — это количество предметов ItemNum (это позволяет, например, представить горсть монет

как единый объект). Затем расположены координаты предмета на карте

XPos, YPos и ZPos.

Далее следуют указатели Prev и Next. Они добавлены для формирования связанного списка структур sMapItem. Следующая пара переменных, Index и Owner, используются при загрузке и сохранении предметов на карте. Index хранит текущий индекс предмета в связанном списке. Если предмет принадлежит другому предмету, переменная Owner хранит индекс родительского объекта (в противном случае значение Owner равно –1).

При загрузке (или добавлении) объекта вы устанавливаете заключительную переменную в sMapItem (Parent), чтобы она указывала

на структуру данных действительного владельца предмета. Концепция связанного списка структур sMapItem показана на рис. 11.8.

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

Структура sMapItem использует и конструктор и деструктор,

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

netlib.narod.ru

497

Глава 11. Определение и использование объектов

ВНИМАНИЕ!

Если вы хотите удалить из связанного списка только один

 

экземпляр структуры

sMapItem, сперва присвойте

переменной Next удаляемого экземпляра значение NULL.

Это гарантирует, что все последующие экземпляры в связанном списке не будут удалены.

В классе cMapICS есть две закрытые функции (GetNextLong и GetNextFloat) используемые для чтения текста и преобразования его в значение типа long или float. Также в классе cMapICS есть восемь

полезных открытых функций. Давайте познакомимся с ними поближе. cMapICS::Load, cMapICS::Save и cMapICS::Free

Согласно своим именам, это трио функций загружает, сохраняет и освобождает список относящихся к карте предметов. Первая из трех, Load,

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

MIL_ItemNum Quantity XPos

YPos

ZPos ParentID

Каждый предмет использует шесть строк текста, и каждый элемент (группа из шести строк) последовательно нумеруется (первый предмет в файле — это предмет 0, второй предмет — предмет 1 и т.д.). Вот пример файла, содержащего два предмета:

// Описание предмета 0:

10 // Номер предмета в MIL (значение long)

1// Количество (значение long)

10.0// XPos (значение float)

0.0 // YPos (значение float)

600.0// ZPos (значение float)

-1 // Владелец (-1 = нет, иначе его индекс) // Описание предмета 1:

1// Номер предмета в MIL

1 // ...

10.0

0.0

600.0

0// Относится к предмету 0 (первому предмету в файле)

Комментарии добавлены только для ясности; в реальном файле их не

будет. Читая список предметов, такой как показан выше, функция Load преобразует текст в числа. Из этих чисел создается структура sMapItem

для каждого загруженного предмета на карте и формируется связанный список загруженных предметов. После считывания данных каждого предмета выполняется сопоставление связанных между собою предметов (с использованием указателя Parent в структуре sMapItem).

498

netlib.narod.ru

Джим Адамс

Здесь нет ничего по-настоящему сложного, так что давайте перейдем прямо к коду cMapICS::Load:

BOOL cMapICS::Load(char *Filename)

{

FILE *fp; long LongNum;

sMapItem *Item, *ItemPtr = NULL;

Free(); // Освобождаем предыдущий набор

// Открываем файл if((fp=fopen(Filename, "rb"))==NULL)

return FALSE;

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

// Бесконечный цикл чтения данных предметов while(1) {

//Получаем номер следующего предмета

//(прерываемся, если больше нет предметов,

//о чем свидетельствует возвращаемое значение -1) if((LongNum = GetNextLong(fp)) == -1)

break;

// Создаем новый указатель карты и привязываем его

Item = new sMapItem(); if(ItemPtr == NULL)

m_ItemParent = Item; else {

Item->Prev = ItemPtr;

ItemPtr->Next = Item;

}

ItemPtr = Item;

//Сохраняем номер предмета в MIL

Item->ItemNum = LongNum;

//Получаем количество

Item->Quantity = GetNextLong(fp);

// Получаем координаты

Item->XPos = GetNextFloat(fp); Item->YPos = GetNextFloat(fp); Item->ZPos = GetNextFloat(fp);

//Получаем номер владельца

Item->Owner = GetNextLong(fp);

//Сохраняем индекс и увеличиваем счетчик

Item->Index = m_NumItems++;

netlib.narod.ru

499

Глава 11. Определение и использование объектов

}

//Закрываем файл fclose(fp);

//Сопоставляем взаимосвязанные объекты

ItemPtr = m_ItemParent;

while(ItemPtr != NULL) {

// Проверяем, относится ли предмет к другому if(ItemPtr->Owner != -1) {

// Находим соответствующий родительский предмет

Item = m_ItemParent; while(Item != NULL) {

if(ItemPtr->Owner == Item->Index) {

// Связываем, устанавливая указатель на родителя

ItemPtr->Parent = Item;

break; // Останавливаем сканирование родителей

}

Item = Item->Next;

}

}

// Переходим к следующему предмету

ItemPtr = ItemPtr->Next;

}

return TRUE;

}

ПРИМЕЧАНИЕ

Подобно

большей

части кода из этой книги, функции

 

класса

cMapICS

возвращают

TRUE, если функция

завершена успешно, и FALSE, если произошла ошибка.

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

Сперва функция Save назначает каждой структуре sMapItem в

связанном списке значение индекса (основываясь на их порядке следования). Первому элементу связанного списка присваивается индекс 0, второму — индекс 1 и т.д. Затем обновляются переменные Owner во всех дочерних объектах и данные записываются в файл:

BOOL cMapICS::Save(char *Filename)

{

FILE *fp; sMapItem *Item; long Index = 0;

//Открываем файл if((fp=fopen(Filename, "wb")) == NULL)

return FALSE;

//Назначаем индексы предметам if((Item = m_ItemParent) == NULL) {

fclose(fp);

return TRUE; // Нет предметов для сохранения

}

500

netlib.narod.ru

Соседние файлы в папке GameProg