• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток!
    Стартовал новый литературный конкурс от "Ордена Хранителей" - "Пираты Миртанского моря".
    Каждый может принять в нём участие и снискать славу и уважение, а в случае занятия призового места ещё и получить награду. Дерзайте
  • Дорогие друзья, год подходит к концу, и пришло время подвести его итоги и наградить достойных

    Не ленитесь, голосуйте в этой теме за тех форумчан, которые по вашему мнению больше всех проявили себя в этом году
    По желанию, аргументировать свой выбор можете в теме обсуждения голосования.
  • Друзья, опубликованы форумные итоги 2024 года!
    Мы старались и трудились для вас!

    Ознакомиться с ними можно здесь

Вопросы по union

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);
}

Create_zCWorld_Test.jpg


Как видим, при нажатии 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):
TestPack1.jpg


При открытии инвентаря вижу следующее:
TestPack2.jpg

При этом, все вещи из запакованной строки, превратились в реальные предметы, и у ГГ стало 30 вещей.
Как видим, всё происходит по первому же требованию.

У НПС после загрузки тоже такое бывает.
Например, здесь мы видим, что у него 2 вещи (топор и фартук кузнеца) находятся в инвентаре, а остальные вещи только в виде строки:
TestPack3.jpg


здесь мы видим, что вещей в строке нет, т.к. они уже отправились в инвентарь:
TestPack4.jpg


открываем инвентарь, видим шесть вещей и один фартук (не отображается, т.к. экипирован):
TestPack5.jpg


ну или вот, 4 вещи и фартук:
TestPack6.jpg


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

Ну и в целом, не рекомендую создавать постоянно новый мир и удалять его после рендера каждого предмета. Это ненужная операция. Лучше его создать перед рендером всех предметов, а по окончанию работы функции - удалить. Либо создать единожды, на всю игровую сессию, как глобальный объект, и никогда не удалять.

И ещё одна заметка. Нужно понимать, что экран можно закрасить всего лишь одной вьюшкой, постоянно меняя её координаты и вызывая функцию рендера Blit().
Да, и последнее, что хотел сказать. Если твоя система будет нормально работать, выйдя за рамки теста, то подумай над альтернативным управлением, кроме мышки.
Чтобы люди, использующие джойстик, не были в пролёте.
 
Последнее редактирование:

DAMROCK

Участник форума
Регистрация
23 Янв 2017
Сообщения
29
Благодарности
7
Баллы
160
Возник вопрос по поводу коллизии, необходимо отследить столкновение zCVob с моделью мира. Потом, в месте столкновения проследить какой материал с текстурой наложен на полигон.
 

DAMROCK

Участник форума
Регистрация
23 Янв 2017
Сообщения
29
Благодарности
7
Баллы
160
С учётом воды или без?
И в целом, что хочешь сделать?
Желательно с учётом воды. На данный момент работаю с камерой, надо просчитать столкновение, чтобы она не вылазила в пустое пространство. Наверное, это как-то делается с помощью AI методов камеры, но там я ничего не понимаю. А про материал- это на будущее, так как рассматриваю возможность сделать повреждение состояний оружия в зависимости от того о какой материал будет совершён удар. Поэтому, тут одно к другому, всё-равно придётся искать доступ к конкретным полигонам level-mesh.
 

Jr13San


Модостроитель
Регистрация
1 Апр 2010
Сообщения
467
Благодарности
293
Баллы
230
На данный момент работаю с камерой, надо просчитать столкновение, чтобы она не вылазила в пустое пространство.
А что, с ней есть какие-то постоянные проблемы, которые можно решить на раз-два? Есть какая-нибудь запись или скриншот проблемы?

рассматриваю возможность сделать повреждение состояний оружия
А где ты собираешься сохранять это значение? Нужно учитывать, что оружие может храниться в виде пачки, а пачка - это один экземпляр oCItem, с определённым кол-вом штук. И как понять какая из штук на сколько изношена?
Также нужно учесть, что вещи могут путешествовать по слотам, инвентарям, сундукам и по миру.

А так, проверка на пересечение оружия с поверхностью делается с помощью трассировки лучей.
Т.е. вычисляется вектор длины, исходящий от рукояти и затем этот вектор трассируется в момент проигрывания анимации удара.
У меня какие-то наработки по этой теме есть, но там решение не в две строчки кода.
Желательно с учётом воды.
А вода то тебе зачем?
 

DAMROCK

Участник форума
Регистрация
23 Янв 2017
Сообщения
29
Благодарности
7
Баллы
160
А что, с ней есть какие-то постоянные проблемы, которые можно решить на раз-два? Есть какая-нибудь запись или скриншот проблемы?
По сути это даже не проблема, просто я перепрограммирую камеру для свободного вращения в режиме интерактивной мыши в новом инвентаре. Так как приходится слипать камеру для работы с ней, айшник камеры вырубается, мне необходимо менять положение камеры через slerp + изменение ориентации так-же. Без учета столкновений, камера игнорирует level-mesh, и проходит сквозь стены.
изображение_2025-01-06_171727787.png
Не знаю понятно будет или нет, но вот здесь камера в стену залезла, как пример.
А где ты собираешься сохранять это значение? Нужно учитывать, что оружие может храниться в виде пачки, а пачка - это один экземпляр oCItem, с определённым кол-вом штук. И как понять какая из штук на сколько изношена?
Также нужно учесть, что вещи могут путешествовать по слотам, инвентарям, сундукам и по миру.
Я это делал через обновление класса oCItem в oCItemEx, При инициализации определённых предметов я задаю личный id каждого предмета, который отсутствует в оригинале, после первого сохранения игры все эти предметы жёстко идентифицированы. Пачки проработаю в механике контейнера, думаю, с этим серьёзных проблем возникнуть не должно, ну там будет видно. Чтобы ответить на этот вопрос конкретно, надо за это взяться, а сейчас только на бумаге.
А так, проверка на пересечение оружия с поверхностью делается с помощью трассировки лучей.
Т.е. вычисляется вектор длины, исходящий от рукояти и затем этот вектор трассируется в момент проигрывания анимации удара.
У меня какие-то наработки по этой теме есть, но там решение не в две строчки кода.
Да мне хотя-бы в общих чертах, что уж-там, главное понять что/от куда вызывать и как оно называется.
А вода то тебе зачем?
честно, не знаю :oops:пока-что...) Но в целом, это может быть важно при работе с коллизией, пока-что не тестировал этот момент.
 
Последнее редактирование:

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.089
Благодарности
1.913
Баллы
320
я перепрограммирую камеру для свободного вращения в режиме интерактивной мыши в новом инвентаре
Хукни и перепиши zCAICamera::CheckKeys
Меняй в нём поля bestRotX/Y/Z и вызывай moveTracker->SetHintMouseUsed

Удары оружием об меш локации детектируются в oCAniCtrl_Human::CheckMeleeWeaponHitsLevel и звук воспроизводится в зависимости от материала.
 

DAMROCK

Участник форума
Регистрация
23 Янв 2017
Сообщения
29
Благодарности
7
Баллы
160
Меняй в нём поля bestRotX/Y/Z и вызывай moveTracker->SetHintMouseUsed
не совсем понятно что именно делают эти переменные и метод.
Удары оружием об меш локации детектируются в oCAniCtrl_Human::CheckMeleeWeaponHitsLevel и звук воспроизводится в зависимости от материала.
так-же, что возвращает CheckMeleeWeaponHitsLevel? как вообще в таких методах понять что они возвращают?
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.089
Благодарности
1.913
Баллы
320
так-же, что возвращает CheckMeleeWeaponHitsLevel?
Возвращает true, если был зафиксирован удар об меш

как вообще в таких методах понять что они возвращают?
Подучить английский и/или стащить где-нибудь исходники готы:confused:
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.089
Благодарности
1.913
Баллы
320
не совсем понятно что именно делают эти переменные и метод.
Простейший пример:

C++:
        if (GetMode() != "CAMMODINVENTORY")
            return THISCALL(Ivk_zCAICamera_CheckKeys)();

        const float mouseMultiplier = moveTracker->frameTime * 5.0f;

        float mx, my, mz;
        zinput->GetMousePos(mx, my, mz);

        bestRotX += my * mouseMultiplier;
        bestRotY += mx * mouseMultiplier;

        moveTracker->bMouseUsed = mx != 0.0f || my != 0.0f;
 
Сверху Снизу