Jr13San
Модостроитель
- Регистрация
- 1 Апр 2010
- Сообщения
- 467
- Благодарности
- 293
- Баллы
- 230
Да, внутри этой функции заключён механизм удаления объекта.Выходит что pWorld->Release() освобождает память?
Ну т.е. когда создаётся новый объект, он имеет лишь одну ссылку (zCObject::refCtr = 1). Если при этом вызвать "Release()", то объект сразу же удалится.
Почему? Потому что Release() - освобождает одну ссылку на объект, а там как раз была одна, тогда остаётся ноль. А отсутствие ссылок на объект (zCObject::refCtr = 0), говорит системе о том, что данный экземпляр больше никому не нужен и его можно удалить, чтобы освободить память для других объектов или переменных.
Также иногда бывают ситуации, когда объект создаётся в одной функции и "передаётся во владение" другой функции, которая периодически обращается к этому объекту.
Либо этот объект может быть "облачным объектом", без каких-то опознавательных знаков и с ним постоянно работают из разных мест. Причём из каждого места работы с объектом, "сообщили системе", что он ещё нужен, с помощью функции "zCObject::AddRef()".
Функция AddRef() - увеличивает число ссылок на объект на одну. Или по-другому, резервирует ещё одну доп. линию для обращения к объекту.
В случае, если в каком-то месте работы с объектом, он больше не нужен, вызывается Release(). И если ещё остаются ссылки на этот объект, он продолжает существовать в памяти, если же ссылок больше нет, то он удаляется.
Нужно ещё отметить, что после отказа работать с объектом, нужно очищать указатель на него, чтобы больше в ту область памяти не обращаться. Потому что к тому времени там может быть уже совершенно другой объект или абстрактный набор байт.
Для этого есть специальный макрос: "zRELEASE(obj)". Он как раз освобождает одну ссылку на объект и обнуляет текущий указатель на него.
Пример работы системы создания и удаления объекта:
C++:
void Game_Loop()
{
// объявляем пустой указатель на объект
// (можно вынести из функции, но уже без приставки статик)
static zCWorld* pWorld = NULL;
// При нажатии клавиши "NUMPAD4"
if (zKeyToggled(KEY_NUMPAD4))
{
cmd << "NUM 4" << endl;
// если указатель пуст
if (!pWorld)
{
// создаём новый мир
pWorld = new zCWorld();
// если создан
if (pWorld)
{
// выводим сообщение
cmd << "Create new zCWorld object (ptr = " << (int)pWorld << ", refs = " << pWorld->refCtr << ")" << endl;
// задаём название миру
pWorld->m_strlevelName = "NEW_WORLD_01";
}
}
}
// При нажатии клавиши "NUMPAD5"
if (zKeyToggled(KEY_NUMPAD5))
{
cmd << "NUM 5" << endl;
// если мир создан
if (pWorld)
{
// выводим сообщение
cmd << "Call Release()" << endl;
// освобождаем его
pWorld->Release();
// и обнуляем наш указатель
pWorld = NULL;
// или всё тоже самое делает:
// zRELEASE(pWorld);
}
}
}
// Перехват деструктора объекта класса zCWorld.
// 0x006200F0 protected: virtual __thiscall zCWorld::~zCWorld(void)
static void __fastcall zCWorld_Destructor(zCWorld* _this);
static CInvoke <void(__thiscall*)(zCWorld*)> pzCWorld_Destructor(0x006200F0, zCWorld_Destructor, IVK_AUTO);
static void __fastcall zCWorld_Destructor(zCWorld* _this)
{
// сообщение для отладки
cmd << "~zCWorld (LevelName = '" << _this->m_strlevelName << "', ptr = " << (int)_this << ")" << endl;
pzCWorld_Destructor(_this);
}
Как видим, при нажатии NumPad4, создаётся новый объект, который имеет свой указатель ptr и число ссылок = 1.
Потом, при нажатии NumPad5, вызывается Release(), она уменьшает число ссылок до 0, затем объект удаляется.
Но перед удалением, вызывается функция деструктора объекта: ~zCWorld(). В ней мы и видим наш недавно созданный объект, судя по названию и указателю на него.
Всё. Дальше можешь сам поэкспериментировать с AddRef() и Release().
Механизм упаковки вещей в текстовую строку, используется, например, во время сохранения инвентарей.Не совсем понял, для чего служит распаковка, ране я прописал только через player->inventory2.GetItem(inumItem).
Если система активна, то содержимое инвентаря сохраняется в виде зашифрованной строки, в которой, для каждого предмета, пишется название инстанции и кол-во предметов данной инстанции. После загрузки сейва, некоторые вещи так и остаются в запакованном виде.
Если система неактивна, то вещи в сейв пишутся как объекты.
По умолчанию, эта система активна, и за это отвечает свойство объектов: BOOL oCNpcInventory:: PackAbility.
При первой же необходимости, вещи из строки обратно распаковываются в объекты класса oCItem.
Возможно этот механизм был введён для экономии расхода памяти, чтобы не держать постоянно в памяти все свойства этих вещей.
Некоторые функции движка, учитывают не только содержимое инвентаря, но и вещи в зашифрованной строке.
Но если делать перебор вещей вручную, то можно не досчитаться некоторых экземпляров.
Можешь посмотреть на механизм в действии с помощью этого тестового кода:
C++:
void GetNumItems(oCNpc* pNpc, int& num, zSTRING& packItems)
{
num = 0;
packItems = "";
if (!pNpc)
return;
zCListSort<oCItem>* node = pNpc->inventory2.contents->GetNextInList();
while (node)
{
oCItem* item = node->GetData();
node = node->GetNextInList();
if (item)
{
packItems += (item->GetInstanceName() + Z ":" + Z item->amount + " ");
num++;
}
}
}
void Game_Loop()
{
if (player && screen)
{
int x = 100;
int y = 100;
int ystep = 200;
int numItems;
zSTRING packStr;
// число вещей, упакованных в строку
int numPacked = player->inventory2.GetNumItemsInPackString();
// способность упаковки вещей (1 - вкл, 0 - выкл)
screen->Print(x, y, Z "player:packAbility = " + Z player->inventory2.packAbility);
y += ystep;
// упакованные вещи игрока (название инстанций и кол-во предметов)
screen->Print(x, y, Z "player:packString( " + Z numPacked + Z " ) = " + Z player->inventory2.packString);
y += ystep;
GetNumItems(player, numItems, packStr);
screen->Print(x, y, Z "player:numItems(" + Z numItems + Z ") = " + packStr);
y += ystep;
// указатель на НПС в фокусе ГГ
oCNpc* pFocusNpc = player->GetFocusNpc();
if (pFocusNpc)
{
zSTRING npcName = pFocusNpc->GetName(0);
screen->Print(x, y, npcName + Z ":packAbility = " + Z pFocusNpc->inventory2.packAbility);
y += ystep;
screen->Print(x, y, npcName + Z ":packString( " + Z numPacked + Z " ) = " + Z pFocusNpc->inventory2.packString);
y += ystep;
GetNumItems(pFocusNpc, numItems, packStr);
screen->Print(x, y, npcName + Z ":numItems(" + Z numItems + Z ") = " + packStr);
// y += ystep;
// упаковать все вещи НПС в фокусе
if (zKeyToggled(KEY_NUMPAD2))
{
cmd << npcName << " -> Pack all items" << endl;
pFocusNpc->inventory2.packAbility = TRUE;
BOOL packEquipped = FALSE;
pFocusNpc->inventory2.PackAllItems(packEquipped);
}
// распаковать все вещи НПС в фокусе
if (zKeyToggled(KEY_NUMPAD3))
{
cmd << npcName << " -> Unpack all items" << endl;
pFocusNpc->inventory2.UnpackAllItems();
}
}
// упаковать все вещи ГГ
if (zKeyToggled(KEY_NUMPAD0))
{
cmd << "Player -> Pack all items" << endl;
player->inventory2.packAbility = TRUE;
BOOL packEquipped = FALSE;
player->inventory2.PackAllItems(packEquipped);
}
// распаковать все вещи ГГ
if (zKeyToggled(KEY_NUMPAD1))
{
cmd << "Player -> Unpack all items" << endl;
player->inventory2.UnpackAllItems();
}
}
}
Например, загружаю ранее сделанный сейв, и вижу, что часть предметов находится в инвентаре (9), а часть осталась в зашифрованном виде(21):
При открытии инвентаря вижу следующее:
При этом, все вещи из запакованной строки, превратились в реальные предметы, и у ГГ стало 30 вещей.
Как видим, всё происходит по первому же требованию.
У НПС после загрузки тоже такое бывает.
Например, здесь мы видим, что у него 2 вещи (топор и фартук кузнеца) находятся в инвентаре, а остальные вещи только в виде строки:
здесь мы видим, что вещей в строке нет, т.к. они уже отправились в инвентарь:
открываем инвентарь, видим шесть вещей и один фартук (не отображается, т.к. экипирован):
ну или вот, 4 вещи и фартук:
В общем, можешь поэкспериментировать с помощью нажатия различных NumPad клавиш, перед этим заглядывая в код, чтобы понимать что должно произойти.
Ну и в целом, не рекомендую создавать постоянно новый мир и удалять его после рендера каждого предмета. Это ненужная операция. Лучше его создать перед рендером всех предметов, а по окончанию работы функции - удалить. Либо создать единожды, на всю игровую сессию, как глобальный объект, и никогда не удалять.
И ещё одна заметка. Нужно понимать, что экран можно закрасить всего лишь одной вьюшкой, постоянно меняя её координаты и вызывая функцию рендера Blit().
Да, и последнее, что хотел сказать. Если твоя система будет нормально работать, выйдя за рамки теста, то подумай над альтернативным управлением, кроме мышки.
Чтобы люди, использующие джойстик, не были в пролёте.
Последнее редактирование: