J
expert
offline
Опыт:
48,447Активность: |
fsgui - создаем инвентарь
Короче эту статью я сделал еще давно, еще до лета, я думал - как же написать статью об инвентаре, но нипридумал ниче лучше как написать его, инвентарь, прямо в статье с коментарием всех действий. Но чем больше я писал тем больше это было похоже на какойто бред понятный только мне одному, потому я както скомкал рабту и бросил в угол, ну теперь я ее достал, мне стало ее жалко...
Несмотря на такой большой рамер статья еще далеко далеко не полностью написана, ну я ее выкладываю, может да и пригодится кому, если будет поддержка то может быть даже допишу... Как писалась статья?Перед тем как написать статью, я почти полностью сделал инвентарь (я его прикрепил к посту под названием MyInventory v0.9 (end)), а потом в статье я пыталя вспомнить как я его делал постояно копирую коды из моего уже сделаного инва, получислось не очень удобно но всеже хоть как то... так что раз все промежуточные коды между null и готовым инвентарем я писал по памяти, то имеется вероятность ошибок, если вы решили делать инвентарь по инструкции то пишите обо всех нестыковках или о всех местах где у вас чтолибо невыходит в статье... |fsgui переводится как "полно-экранный графический пользовательский интерфейс". Конечно, вам уже приходилось видеть нечто подобное от Zibad'ы, Netrat'а или т.п. людей В данной статье мы создадим свой fsgui, если сказать точнее - Инвентарь. Статья полезна и не только по практическому руководству по fsgui, но даже просто как обучающий материал о возможностях джаза. Сначала будет много рутинной работу с обьектами в РО, разьясненю концепции и импарта, и как будем готовы приступем непосредствено к кодингу... Когда я выкладываю функции что выклдывал раньше но с некоторыми дополнениями то помечаю их плюсиком Первая часть - НачалоТеперь по обсуждаем - как наш инвентарь будет выглядеть... Конечно это очень сложный вопрос, потому что когда вы делаете инвентарь для какой-то карты, то за время, которое вы тратите на создание инвентаря, вы меняете кучу идей и сюжетных линий в игре, и вид инвентаря может измениться, и вам придется его переделывать. Не основательно конечно, но все же потребует времени и сил. Так как же сделать инвентарь универсальным? чтобы можно было изменить положение какой-либо вещи только одним взмахом руки? Для этого я придумал использовать массив строк, т.е. каждый строчка содержит символы, определяющие положение в инвентаре какого либо объекта, к этому массиву строк мы пишем алгоритм, который анализирует каждый символ в этом массиве, и создает инвентарь по той информации, что получает из этого массива. Вот как выглядит наш конечный инвентарь в виде массива строк: » Массив строк Код:
Комментарии между элементами массива служат чисто для наглядного удобства пропорций ячеек. Что вообще в реале представляет будующий наш и все другие инвентори? Джаз не дает возможность рисовать какие-то картинки на экране и услеживать положение и щелканье мыши по любому из этих кортинках как вам взумается. Потому fsgui это просто рисунок на земле. Т.е. вы создаете камеру перпендикулярную земле и смотрящую на нее. И на облости видемости камеры создаете декорации, и на некоторых декорациях еще и трекаблы. Примечание: предпологается что читателю известо что такое трекаблы и их особености и слабости, если же вам это не известно то сначало советую пойти почитать статьи по ним. (вот статья на трекаблы (ссылка)) Т.е. красивая рассовая рамочка, все слоты, и все остальное, каждое в отдельности это отдельная декорация. Всего таких тиов декораций будет очень много, нам прийдется изрядно помучится что создать в редакторе обьектов типов декораций по количеству всех возможных текстур предметов в игре, и + еще не относящиеся к игре модели границы и ячеек и т.п. Мне лень считать и назвать вам точную цыфру, но типов декорация будет 250 и более. Именно поэтому переносить или начинать делать инвентарь проще на совершено пусто карте, а только после заниматся всем остальным... Ощутимо большая часть этих декорация будет выполнять роль иконок предметов в нашем инвентаре. Т.е. первое что нам нужно сделать в инвенторе, это создать декорации для иконок предметов. (Однако я их уже подготовил, она находятся в файле StandObject.w3o прикрепленому к этому посту) |Далее когда у нас предметы будут готовы, нам надо както связать эти предметы с декорациями которые их представляют. Также у нас возникает проблема - несуществует способа узнать описание предмета, потому нам прийдется атачить к предмету также информацию о его описании. Также в нашем инвентаре будет отображатся количество зарядов у предмета, но нельзя сразу 100%-тно определить что предмет имеет заряды или он постоянный, потому нам надо также атачить к предмету Булевую переменую true(имеет)/flase(неимеет) заряд. Чтобы сделать все это нам понадобится RB и SCV Примечание: предпологается что читателю известо что такое RB и SCV, если же вам это не известно то сначало советую пойти почитать статьи по этому. Давайте еще раз обратим внимание на то как нам приатачить тип декорации к предмету... На первый взгляд все просто, атачим через кеш тип декорации к каждому предмету, если создадим новый предмет, создаем еще одну декорацию с нужной текстурой, и атачим ее к новосозданому обьекту, и т.д. НО, этот способ неуниверсален, т.е. прийдется создавать кучи декораций с одной и тойже текстурой, потому что предметы иногда имеют одинаковые текстуры, а если нестандартных предметов очень много? то это может вызвать путаницу. Потому мы будем атачить к предмету "путь к его текстуре". А к текстуре будем атачить "Тип декорации". Если представить это в виде базы данных, то мы считай разделили одну таблицу на две с более меньше повторяющимися данными, в програмировании этот процес называется нормалицицией баз данных. Вторая часть - ПодготовкаНаш инвентарь будет использовать Jass New Gen Pack о нем можно узнать по этой ссылке. Из его способностей нам понадобится только упрощеное обьявление глобалок, потому если вы не хотите использовать JNGP, то создавайте глобалки в редакторе переменных и в коде добавляйте им прификс "udg_". Щас мы будем покачто только подготавливать карту к созданию инвентря, т.е. уконфортим наш рабочий стол и выложим все инструменты на виду. Я люблю делать системы по отдельным триггерам, т.е. система в одном тригере другая система в другом, мне такой подход кажется удобнее, но вы смотрите сами... Т.е. наш инвентарь будет распологатся только а одном триггере. |Эту часть статьи мне лень писать, сдесь должно описыватся как создавать объекты в РО, а также описисывается весь импорт, принцепи читатель и сам после вставки в карту может посмотреть что они из себя представляют и как все реализованно |Создадим новую карту, импортируем туда нашы обьекты StandObject.w3o Потом импортируем в карту все обьеты из папки StandImports У всех импортируемых файлов уберите из строчки пути "war3mapImported\" Кроме BTNInventory.tga и DISBTNInventory.tga, для них нужно заменить "war3mapImported\" на "ReplaceableTextures\CommandButtons\" Потому что это единственные текстуры использующиеся в панеле упровления игрока... добавте одному или нескольким героям абилку инвенторя, и поставте их на карту |Создадим триггер под названием "MyInventory", в нем будет находитя вся система инвентаря, теперь создадим триггер "MapInitialization", в нем просто в Gui-шной форме создайте событие загрузки карты, и добавте действие на открытие поля видемости всей карты. Теперь зайдем в триггер "MyInventory", Он у нас покашто пустой, сделаем его еще более пустее - конвектируем его в текст, и удаляем весь его код, оставив только функцию Код:
Т.к. без нее триггер просто не может существовать... Для справки: Функция InitTrig_MyInventory() выполняется в самую первую очередь до всех событий Map Initializtion Все глобалки будем добовлять только в триггере инвентаря в самом верху кода. Добавим Кеш. Код:
Теперь нам нужно создать этот кеш, для этого нам пригодится функцию которая выполняется всегда при загрузке карты. Код:
|Теперь зделаем функцию в которой в массив строк будет заносится условное оформление инвенторя: Создадим ее в Costum Script'е карты, потому что мы больше к этой функции не вернемся, но чтобы она немешалась создадим именно там. » функция InvStructureConfig() Код:
InvStructure - глобальная переменая, добавим ее к списку глобальных переменных в триггере инвентаря. Код:
а также вызовем эту функцию в функции InitTrig_MyInventory() чтобы она выполнялась при инициализации. Код:
|Сделаем функции для добавления записей в таблицу : Код:
InventoryInitItemDB_Func(ItemType, Description, Cost, Charge, Texture)
InventoryInitDestDB_Func(TypeDest, Texture)
InventoryInitIconDB_Func(TypeUnit, Texture)
Последняя функция нужна для того чтобы отображалась иконка активного героя, по самому герою мы получим текстуру, а потом по текстуре получим Декорацию иконку при помощи функции выше Теперь добавим туда же мою базу данных по предметам Она прикреплена к посту с названием DataBase.j Вставим ее в Costum Script'е по тойже причине... теперь опять подкоректируем функцию InitTrig_MyInventory() Код:
|Теперь нам нужно создать функцию которая создает инвентарь. Так как у нас инвентарь будет мультиплеерный, то функция должна принимать не только позицию инвенторя, но и группу игроков для которых будет создан инвентарь. Также функция будет еще принимать равкод абилки которая активирует инвентарь. Код:
А теперь в триггере инициализации в конце через костум скрипт, вызываем эту функцию cs: call CreateInventoryForForce(GetPlayersAll(), 0, 0, 'A000') Для справки: Коды триггеров выстраиваются по пряду сверху вниз как в редакторе триггеров, нужно чтобы обьявление функции в триггере MyInventory, было выше чем вызов ее в триггере инициализации, т.е. он должен быть ниже, если всеравно выдает ошибку перезапустите карту... Все, подготовка завершена, теперь можете сохранить карту, если не сохранилась значит сделали чтото не то... Код:
Третья часть - "Рисуем" инвентарьТеперь у нас есть одна проблема - "каким способом нам рисовать инвентарь?" Напомню что в инвентарях Zibad'ы и Netrat'а создается инвентарей по количеству игроков, т.е. если игроков 12, то инвентарей будет 12... Так делается чтобы все игроки могли пользоватся инвентарем одновремено, иначе бы если бы 2 игрока запустили инвентари одновремено, то один из играков видел все предметы и действия другово игрока т.к. они распологались на одной территории. Получается они занимают в 12 раз больше места чем один инвентарь, и хоть у нас инвентарь в 5 раз меньше чем ихний это всеравно не порядок... Ну а мы сделаем иначе, мы создадим инвентарь на одном месте, НО сделаем так чтобы если игрок пользовался инвентарем, то все его действия были бы невидны другим игрокам, как мы это сделаем? А через функцию GetLocalPlayer() Примечание: Предпологается что читатель имеет о функции GetLocalPlayer() хотябы базовые знания, знает что это и как работает Напомню только что использование этой функции чревато Дисинхранизацией, потому использовать ее нужно очень осторожно иначе это вызовет много проблем. |Помните мы решили в целях удобства и универсальности сделать так чтобы инвентарь можно было рисовать в коде в виде Строковых переменных. Теперь же мы напишем функцию которая по этим строкам создает инвентарь. Для начала создадим пару переменых в которые запишутся входящие параметры функции CreateInventoryForForce(), т.к. мы будем обращатся к ним не однократно Код:
Теперь нам нужно заполнить эти переменые значениями, в функции, в которой создается инвентрь CreateInventoryForForce(Force, invX, invY, AbilCode) вот в ней и будем инициализировать эти переменые Код:
Также имейте ввиду что нам не нужно создавать инвентари для компьютеров или неиграющих играков, такчто имеет смысл подправить ту force что мы передаем в функцию Код:
Таким образом мы изключаем из группы играков всех неиграющих и всех нелюдей. И получится что из всех игровов которых мы передали - GetPlayersAll(), во время теста, передастся только один игрок - мы. |Помните я расказывал об инвеноре Zibad'ы и Netrat'а? о том что там создается инвентарь для каждого игрока Предположим нам нужно создать инвентарь для всех 12 играков, и предположим что оформление одного инвенторя состоит из 300 декораций Напоминание: Имейте ввиду что оформление инвенторя зависит от рассы игрока, т.е. если это эльф, то оформление будет другое в отличии от например нежити. Т.е. для всех играков получается создается от 300 до 3600(300*12) декораций. Теперь расмотрим наш случай, если мы будем пользоватся GetLocalPlayer(), и создадим инвентари для всех играков на одном и том же месте. Фишка в том что нам не надо будет создавать более одного инвентаря каждой рассы играков т.е. мы на одном месте создадим инвентарь для Людей, Орков, Нежити и Ельфов. А потом просто скроем все декорации от всех Играков, и сделаем их видимыми только для игроков соответствующих расс. Получается декораций у нас будет от 300 до 1200(300*4) декораций Тогда сначало узнаем какие именно расы у нас присутствуют в нашем ForceInv Код:
По умолчанию все элементы массива равны false Наша задача сделать так чтобы Если в ForceInv существует раса Людей, то RaceExists[0] = true -||- Орков, то RaceExists[1] = true -||- Нежити, то RaceExists[2] = true -||- Ельфов, то RaceExists[3] = true Сделаем это в функции CreateInventoryForForce(Force, invX ,invY) Код:
строчка set RaceExists[H2I(GetPlayerRace(p))-1] = true равносильна этому: Код:
потомучто RACE_HUMAN, RACE_ORC, RACE_UNDEAD и RACE_NIGHTELF имеют хендлы 1, 2, 3 и 4 соответствено. Теперь нам извесны все расы для которых нам нужно создать инвентарь |Далее нам нужно написать анализатор массива InvStructure Напишей его в тойже функции CreateInventoryForForce(Force, invX ,invY) Вот как он будет выглядеть: Код:
Как видите мы просто перебираем каждый символ из этого массива строк. Там Где написано //elseif simvol == "Другой Символ" then //Действия Можно вставить обработку следующего символа и потом следующего и т.д. |Теперь нам нужно сделать оформление инвентаря, т.е. рамки иконки и черный фон. Для этого нам потребуется новая функция которая создает все 4 декорации для разных расс и показыывает их нужным игрокам. Вот эта функция: Код:
т.е. функция принимает координаты и все типы декораций для каждой рассы, для какой рассы какая декорация видно по названиям пораметров. и теперь создадим эти декорации Замените Код:
на это: Код:
Думаю код очень нагляден, для каждого символа создаем свои декорации. И в функции CreateInventoryForForce(Force, invX, invY, AbilCode) прямо перед вторым циклом ставим еще такую строку Код:
Декорация 'C000' - просто черная большая пластина. Теперь запустите в игре, посередине карты вы увидите маленький инвентарь, И вы будете видеть его по разному изходя из того какой вы рассы, можете зайти игроком другой рассы и проверить. Четверая часть - Делаем "Запуск" и "Выход" из инвенторяТ.к. у нас появляется 2 состояния игрока "Инвентарь открыт" и "Инвентарь закрыт" Потому создаем глобальную переименую: boolean array OpenInv OpenInv[0-11] - Возвращает значение того "открыт ли инвентарь в данный момент у игрока 0-11?" Также нам еще потребуется таймер Код:
Этот таймер создадим и запустим в функции CreateInventoryForForce(Force, invX, invY, AbilCode) Для начала напишем функцию которую будет выполнять таймер: Она будет устанавливать камеру на инвентарь для всех играков из ForceInv инвентарь у которых открыт. Код:
В функции нет никакого цикла на перебор всех игроков, но одновремено с этим она обрабатывает каждого игрока в отдельности, это одно из преемуществ GetLocalPlayer() А теперь создаем и запускаем этот таймер Код:
Т.е. этот таймер будет активирован на протежении всей игры, и чтобы активировать его для какогото игрока, надо лиш только присвоить значение переменной OpenInv true, для нужного игрока. |Теперь нам нужно создать 2 триггера 1) Вход в инвентарь при касте способности 'A000' 2) Выход из инвенторя при нажатии кнобки Esc Но перед тем как задумыватся о триггерах, давайте просто создадим функции Открытия/Закрытия инвенторя Для начала создадим еще одну глобалку, которая отображает для какого Юнита у нас сейчас отрыт инвентарь Обратите внимание:Мы создаем функцию нисвязвную с никакими событияи отдельно, а при событие будем выполнять ее, именно так нужно всегда делать, создавать свой своеобразный api, т.е. поясню на текущем примере, у нас будет функция Открыть() и Закрыть(), при сработывнии события мы используем ту или иную функцию и в этом ничего страного, но алюс в том что эти функции будут еще доступны вне движка инвенторя, тому кто будет этим инвентарем пользоватся Код:
Открытие: Код:
Функция принимает юнита, т.к. именно юнит является основным пораметром для инвенторя, т.к. именно у каждого юнита содержится разный набор предметов. set OpenInv[id] = true Этим действием мы считай уже Открыли инвентарь, т.к. таймер ViewCamera постояно каждые 0,05 сек вызывает функцию ViewCamera_Func() И если эта переменая ровна true, то и устанавливает для этого игрока камуру в нужное положение. После мы еще раз вызываем функцию ViewCamera_Action() чтобы нам не пришлось ждать от 0 до 0,05 секунд до следующего тика таймера, как камера переместится, а все сработало сразуже. Закрытие: Код:
Все должно быть понятно. Устанавливаем переменной OpenInv значение false и таймер перестанет выполнять свои действия для этого игрока Далее используя GetLocalPlayer() устанавливаем стандартное положение камеры в позицию юнита который открывал этот инвентарь. Ну и обнуляем переменую в которой содержится юнит для которого открыт инвентарь. Сама функция уже принимает не юнита а игрока, т.к. именно игрок уже является главным пораметром для закрытия, т.е. чтобы закрыть инвентарь нам необязательно передавать какогото юнита, достаточно передать игрока инвентарь которого мы закрываем. С этими функциями пожалуй все, теперь перейдем к триггерам... |Функция CreateInventoryForForce(Force, invX, invY, AbilCode) у нас и так уже сильно разбухла, и еще больше разбухнет в будущем, потому сделаем маленькое ответвление: Код:
Мы сделали функцию CreateInvTriggers() в которой будем создавать все подобные прочии триггеры, и вызываем эту функцию в конце функции CreateInventoryForForce(Force, invX ,invY) |Давайте на немного отвлечомся, и посмотрим на эти две функции Код:
Первая функция Автомотически создается в триггере при использования события Боевая Еденица - Generic UnitEvent в GUI Вторая функция Тоже автомотически создается но при использовании события Игрок - Cinematic Skipped Мы их немножко поправим в тот вид в котором будем использовать Код:
т.е. события устанавливаются только для играков для которых создан инвентарь Всуньте эти функции в нестандартный код чтобы не мазолили глаза |Ну и теперь непосредствено создаем триггеры Начнем с открытия инвенторя: Код:
Видите, все довольно просто, создаем тригер, нацепляем ему Функцию-Действие, в которой вызываем только функцию открытия инвентаря которую мы уже написали, принимающую юнита который включил эту способность. А также подцепляем к этому триггеру Условие: 1) Чтобы Кастуемая способность была InvAbilCode т.е. инвентарь наш. 2) Чтобы инвентарь у Владельца этого юнита еще небыл открыт И еще один триггер зкрытия инвенторя: Код:
Ну тут до смехотворного все просто, небуду даже коментировать. Теперь попробуйте запустить в игре и посмотреть как это будет выглядеть. Имейте в виду что у нас будет еще очень много функций, то что мы щас написали не тянет и на четверть, и потому на вашем месте я бы группировал функции в группы по смыслу, и начало каждой новой группы отмечал бы какимнибудь сильным, бросающимся в глаза коментарием, содержащим название группы. Пятая часть - Делаем Слоты инвенторяВ этой главе мы начнем работать с трекаблами. Во первых в глобальные переменные добавим еще три переменые: Код:
В InvStructure[] ячейка у нас обозначается символом "X" Потому в функцию CreateInventoryForForce(Force, invX ,invY) В тот большой if, добавим еще один elseif: Код:
CteateSlotInventoryForForce (X, Y, Index) - Функция создания ячейки, принимает Координаты этой самой ячейки а также уникальный номер, т.е. у каждой ячейки будет свой номер от 0 до (количство ячеек - 1) И потом когда мы будем делать закладывание предметов в этот инвентарь, Они будут ложится в ячейки исходя из их индекса, попорядку. Напишем тело функции CteateSlotInventoryForForce (X, Y, Index) Во первых запоминаем координаты Слота: Код:
На этом мы пока остановимся, и обсудим одну вещь косающуюся трекаблов и без которой сейчас не сможем обойтись |Всеже предпологается что читатель знаком с основами Трекаблов, и знает как создавать триггеры их использующие Нужно сказать что в функции-действии триггера с событием Нажатия/Наведения на трекабл, можно получить только сам трекабл функцией GetTriggeringTrackable() Если сказать с другой стороны то - несуществует способа узнать какой игрок нажал на трекабл и всю прилогающуюся информацию о его позиции и все, трекаблы очень скудны в варе по количеству функций работающих с ними. Наша с вами основная проблема это определение игрока который нажал на трекабл Т.е. допустим у нас играют 12 игроков, и они одновремено включили инвентарь, один из игроков нажмет на трекабл, но как узнать какой игрок нажал? И для какого игрока выполнять действия в результате нажатия? Если мы будем через GetLocalPlayer() создавать трекаблы для отдельных играков, то это вызовит Десинхронизацию. т.е. надо в каждой нужной позиции создавать по 12 трекаблей для всех играков, и через кеш дать им значение Номера игрока которому они принадлежат, и потом "показать" их только каждому игроку в отдельности, т.е. например как мы делали декорации. Но тут новая проблема, у декорации есть действие ShowDestructable() А вот аналогичного действия у трекаблов нет. Однако, можно сделать так: Код:
Да, да, это будет работать... Т.е. получается Мы создаем Трекабл для всех играков Но для третьего игрока созданый трекабл будет иметь модель "square.MDX" А для всех остальных игроков у трекабла не будет модели Т.е. если я приатачу через кеш к этому трекаблу значение 2, т.е. номер игрока то потом используя его в событии триггера, я смогу получить трекабл функцией GetTriggeringTrackable(), и по ней из кеша дастать номер игрока которому он принадлежит. Т.е. надо создавать до 12 трекаблей на одной и тойже позиции, но показывать каждый трекабл только одному из этих 12 играков |Теперь продолжим заполнение функции CteateSlotInventoryForForce (X, Y, Index) Когда мы впервые пишем эту функцию подсознательно для каждого нового трекабла хочется создаеш новый триггер, и только потом понимаеш что нафиг это нужно Т.к. все триггеры отдельно Кликанья, отдельно Наведения юзают одну и туже функцию И результат выполнения этой функции зависит только от самого нажатого трекабла Потому ВСЕ трекабли-слоты инвенторя будут обробатывать только 2 триггера Давайте обьявим их в глобалках Код:
И создадим их в функции где у нас создаются триггеры, т.е. в CreateInvTriggers() Код:
функция GetOpenInvTk() подобна функции GetOpenInvPl() что мы делали раньше, только подработана для данной ситуации. В ней вы видите пример получения номера игрока из трекабла. Мы натягиваем на триггер только действие и условие, т.е. они будут в одном экземпляре, а вот событий будет много (КоличествоСлотов*Количество игроков), их мы натяним на триггер в функции CteateSlotInventoryForForce() Но имейте ввиду что это еще только пол пути, мы еще не создавали ни трекаблов и неатачили им никаких номеров, это мы сделаем в функции CteateSlotInventoryForForce (X, Y, Index) Код:
|Теперь чтобы увидеть хоть какойто результат нашей работы сунем какиенибудь действия в функции InvHitTrackableRucksack() и InvTrackTrackableRucksack() Код:
Это позволить хоть какнить оценить работу функции, можете даже потестить в мультиплеере Шестая часть - Юнит подбирает предметВсегда перед тем как чето делать надо подумать, а в каком виде мы будем хранить предметы? Долеко не все, но некоторые, делали инвентарь храня только тип предмета, т.е. юнит подбирает предмет -> атачить тип предмета к юниту (Это проще, потому что тип это integer) -> предмет удалить Когда мы достаем из инвентаря этот предмет то мы создем новый предмет по типу который запомнели. На первый взгляд никаких проблем нет, но в результате этого хендл обьекта меняется, т.е. если вы удалите предмет, его хендл освободится, и его место может занять другой обьект, а когда создадите, то он создатся уже с новым хендлом. Если обьеснить на примере - у вас предмет (например - ключ) хранится в переменной, задача доставить этот предмет к воротам для их открытия, вы запоминаете предмет, и теперь он хронится в переменной, но если вы сунете его в инвентарь, то он удалится из той переменной, и создастся новый предмет. И тригер на поднос ключа к воротам не будет работать т.к. это по сути новый предмет Конешно логичнее было бы проводить проверку подноса к воротам не самого предмета, а Типа предмета Но тут все зависит от случая, а если у нас например одинаковые типы переменых служат для разных целей, и одинаковые по типу ключи открывают разные двери. То тут это вызовет проблему Потому мы и будем запоминать не тип предмета, а сам предмет, а т.к. у нас предмет фактически продолжает существование, то нам прийдутся сунуть его куданибудь и скрыть от постороних глаз. Я затронул эту тему о хранени типов переменных потому что она проще реализуется на кеше, т.е. атачим к юниту просто тип - интегер, и не нужно будет мучится с RB Но мы сделаем инвентарь не на кеше а на массивах, и потому на это нам совершенно пох, но всеже стоило упомянуть. Но раз мы делаем инвентарь не на кеше а на массивах то тут будут свои проблемы. как вы помните мы договорились делать инвентарь для каждого юнита, т.е. если выберем юнит и запустим инвентарь, то в нем будут только предметы которые принадлежат чисто ему, выберем другово - предметы будут другие. И вот возникает пролема, "как атачить все предметы к юниту через _массивы_?" допустим мы использовали бы кеш, то тогда мы просто первым индексом использовали Хендл юнита, а вторым порядковый номер в инвенторе, и под каждый прядковый намер от 0 до AmountSlot засунем в инвентарь хендл предмета. Это самый простой способ, но если мы делаем на массивах - забудте о строках и хендалх. Мы обойдемся одним массивом предметов. максимум мы можем хранить 8190 предметов Допустим что для одного юнита нам за глаза хватит 150 предметов, это даже будет выглядеть чересчур нагроможденным, но возмем именно 150 тогда R2I(8190/150)=54 Т.е. получается что используя один массив мы можем обеспечить инвентарь для 54 юнитов. Нам еще прийдется каждому юниту приатачить индекс с которого начинаются его ячейки в массиве, т.е. например 0 или 1 или 2 или ... или 53. Предмет будут хронится в массиве ItemInUnit[] от i*150 до i*150+150 - [0-150], [150-300], ..., [7950-8100]. Но тут все говорит о том что нам надо будет вручную давать юниту этот индекс, т.е. его же нельзя дать сразу всем юнитам на карте, потому что размер ограничен. Наи прийдется написать свою функцию UnitAddAbilityInventory (по нозванию понятно) и еще сделаем один массив булеонов, ячейки которого [0-53] будут показывать свободно ли это место для нового юнита или оно занято уже созданым. Код:
Теперь напишем функцию UnitAddAbilityInventory(), но перед этим давайте зайдем в РО и удалим у нашего юнита способность инвентаря, т.к. мы всеравно будем добовлять ее вручную. Код:
Все должно быть понятно, перебираем массив UnitOccupied[], и ищем свободные ячейки, как только нашли то дать юниту абилку инвенторя, пометить ячейку занятой, и приатачит через кеш его индес массива ItemInUnit[]. Функция возвращает true если добавить удалось, и false если по каким бы то небыло причинам этого сделать не удалась. ну раз зошла тема то и напишем функцию для удаления абилки Код:
Все должно быть понятно, объясню только что это за цикл - он обнуляет все ячейки массива ItemInUnit[] в которых содержатся предметы данного юнита, чтобы когда следующий юнит занял его место у него небыло такихже предметов. Функция также возвращает true или false соответствено удалось или неудалось (например абилки этой небыло у юнита) удалить эту абилку. и теперь в триггере инициалицации В конце после создания инвенторя добовляем строку cs: call UnitAddAbilityInventory(<Ваш юнит>) можно чтобы сэкономить себе время на время теста, сделать таким образом: Код:
Все, теперь мы можем при желании хранить предметы в этом массиве, теперь нужно написать обработчик события "юнит подбирает предмет" |Но перед тем как создать триггер, напишем саму функцию добавления предмета к юниту. В таком виде: function UnitAddItemInv takes unit u, item itm, boolean CustomInv returns boolean u - собствено юнит которому даем itm - предмет что даем CustomInv - Булевая переменая true (Положить предмет в наш нестандартный инвентарь) / false (Положить предмет в стандартный инвентарь) И сама функция возвращает булевую переменую true (Предмет взян) / false (предмет взять невозможно) Код:
В будущем мы эту функцию конешно еще будем доробатывать... Теперь создадим триггер, тобиш вернемся к функции CreateInvTriggers() Код:
Посмотрите, в этот раз мы не пользуемся conditionfunc как во всех других триггерах, а пользуемся filterfunc Отличие между ними в том что в conditionfunc доступно больше функций для проверки увливия, например для данного случая это GetTriggerUnit(), GetManipulatedItem(), и т.д. а в filterfunc для данного случая только одна GetFilterUnit(), что нам пока будет достаточно Ну и естествено делать через filterfunc это оптимальнее. В ней мы проверяем чтобы событие сробатывало только для юнитов у кого есть абилка инвенторя, и в действиях все просто делается одной функцией которая должна быть понятна. Все, мы все сделали, предмет добовляется и все такое. Теперь другая задача - Нам нужно отобразить этот предмет в инвентаре. |Перед тем как начать чтото вклеивать в движок инвенторя, надо сначало написать это, т.е. так как мы делали раньше, и всегда так нужно делать не только в инвенторях, написать сложную функцию, а потом из них складывать инвентарь. Мы напишем функцию "Отображения" предмета в инвентаре function CreateIconItemInv takes player p, integer position, item itm returns nothing Функция принимает Игрока, в инвенторе которого будем отображать иконку, точнее она будет создаватся для всех инвентарей, но отображать мы ее будем только для игрока p Еще функция принимает позию ячейки которую нужно отобразить, т.е. ее порядковый номер, мы его уже не раз использовали но просто напомню что это тот номер что дается ячейки через пораметр этой функции function CteateSlotInventoryForForce takes real X, real Y, integer Index returns nothing В функции CreateIconItemInv(p, position, itm) Мы будем впервые использовать базу данных что делали в самом начале стайтьи. Код:
Теперь подумаем, вот у нас будут декорации-иконки, отображать предметы в инвентаре, но они же не могут вечно быть создаными в инвенторе, т.е. если мы открываем инвентарь - они создаются, если закрываем, они удалятся. Чтобы удалять их после создания нам потребуется еще массив в котором они вcе будут хранится Т.е. создадим еще один массив Код:
Как видно из коментария декорации атачутся не к юнитам (как было в случае с глобалкой ItemInUnit), а к игрокам. Код:
Имейте ввиду: При чтении и записи из кеша нельзя использовать ключи "" или null. если же таковые будут использованы (Если пораметр itm будет равен null) произойдет крах игры, и вас выкинет из нее с ошибкой. Т.е. перед тем как создать декорацию-иконку в указаной позиции, нужно проверить а существует ли предмет у юнита в этой позиции. Ну и сделаем функцию-напарницу - для удаления: Код:
|Т.к. мы уже решили что все декорации иконки будут создаватся при открытии инвенторя и удалятся при закрытии то нам прийдется дописать функции OpenInventory(unit) CloseInventory(player) Которые мы написали ранее. в первой функции нам нужно отобазить все предметы, при помощи декораций-иконок, которые присутствуют у юнита. а во второй удалить эти декорации-иконоки по выходу из инвенторя. Открытие: Код:
т.е. перебираем все предметы которые принадлежат юниту и показываем их в нужной позиции Закрытие: Код:
Перебираем все предметы которые могут находится в инвентаре и удаляем их. Вот и все, теперь можно нагладно посмотреть, взять парочку придметов и вы увидите что они у нас в инвентаре выстраиваются по порядочку... В следующей главе мы сделаем так чтобы их можно было перемещать из слота в слот. Седьмая часть - Перемещаем предмет из слота в слотВ этой главе оснавная функция с которой нам прийдется работать, это функция InvHitTrackableRucksack() Она у нас есть, и срабатывает каждый раз когда мы нажимаем на трекабл, мы также можем узнать номер нажатого трекабла, а также и игрока который нажимал. Самое главное что сдесь понадобится, это глобальная целочисленная переменая, которая обозначает какой из слотов у нас выделен, если же у нас никакой предмет не выделен то эта переименная будет ровнятся "-1" Это глобальная переменая естественно будет массивом, от 0 до 11, т.к. у каждого игрока может быть свой выделеный слот. "Выделенный" это значит вокруг этого слота будет идти такая мешура, похожая на автокаст, это отдельная декорация которая имеет модель ввиде эффекта, но у каждой расы своя цетом люди - синия 'С115' орки - красная 'С116' нежить - фиолетовая 'С117' ельфы - зеленая 'С118' Чтобы динамически удалять и создавать эту декорацию нам и для нее прийдется создать глобальную переменную, также от 0 до 11 Код:
Напишем функцию CreateEffectSelect(player p, real X, real Y) Которая создается в позиции (X,Y) декорацию для игрока p, естествено тип декорации зависит от расы этого игрока, а также после ее нужно будет скрыть отовсех и показать только игроку p. Код:
Заметте что мы удаляем декорацию-эффект если он уже существует, т.е. не может у нас быть выбрано одновременно 2 и более слотов. По умолчанию переменая выделеног слота ровна 0, что неправельно, она должна быть ровна -1, и должна быть такой всегда при открытии иненторя самое разусеон это ручками выставлять ей такое значение в функции создания инвенторя Код:
и в функции закрытия инвенторя Код:
Ну а теперь смотрите что напишем в функции InvHitTrackableRucksack() Код:
вуаля... Код:
Отредактировано Jon, 18.12.2008 в 13:29. |
01.08.2008, 22:08 | #1
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
далеко не полностью, ну четверть мб
ну незнаю, вроде для меня кажется простой вещью, а кратко написать немогу, что даже сам начал путатся, потому мне казалось что статья не удалась совсем |
01.08.2008, 22:20 | #2
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
Jon, прочитал только самое начало, дочитал до трекаблей. Но ведб их же нельзя удалять, как быть, если я хочу заюзать это в мультиплеере?
akkolt добавил: Кстати, я раньше не понимал, как это осуществлено, а щас всё понятно =) Респект! Отличная статья! |
01.08.2008, 22:29 | #3
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
Цитата:
все будет работать в мультеплеере, читай дальше Цитата:
если ты меняеш расу сам, вид панели упровления не меняется, потому корректнее былобы оставлять все как есть Цитата:
нельзя |
|||
01.08.2008, 22:41 | #4
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
Jon, нет почему... можно же менять декорации в соответствии с расой игрока, тогда и интерфейс инвентаря будет меняться.
|
01.08.2008, 22:49 | #5
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
просто еще добавлю что инвентарь не такой лагуий как у зибады или нетрата, т.к. все с оптимизироано как можно лучше
также в статье все это говорится о надо было в самый верх написать что в инвентаре используется много нестандартных идей если все реализовать правельно, то несмотря на то что инвентарь будет распологатся на земле, то deadZone непонадобится, даже если инв будет прямо на глазах в центре карты также очень гибкая настройки, и множество фукций обращение к инвентарю из вне Jon добавил: akkolt интерфейсо то инва бдет менятся, а интерфейс панели провлни нет, и какой смысл |
01.08.2008, 22:55 | #6
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
Jon, не совсем понял. Я хочу сказать, что можно менять интерфейс инвентаря, заменяя декорации. Т.к. из твоей статьи я узнал, что все рамки и украшения - это ни что иное, как эти самые декоры. Тоесть менять интерфейс инвентаря не составит особого труда. ИМХО.
|
01.08.2008, 22:59 | #7
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
akkolt да млин, ты не втыкаеш, почему по твойму для каждой расы сделано разное оформление?
потомучто сама панель управления, варовская, стандартная, имеет разное оформление для разных рас, для инвентаря я только импортировал модели, все текстуры моделей стандартные, потому инвентарь занимает так мыло места в килобитиках если ты поменяешь оформление инва, то оформление панели управления останется прежднее, и это будет некрасиво, и это вообще не нужно |
01.08.2008, 23:02 | #8
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
Jon, дыг я же сказал менять оформление инвентаря в соответствии с "РАСОЙ ИГРОКА".
Тоесть не интерфейс вара будет подстраиваться под инвентарь, а наоборот - ОФОРМЛЕНИЕ ИНВА БУДЕТ ПОДСТРАИВАТЬСЯ ПОД РАСУ ИГРОКА. akkolt добавил: Объясняю на примере: if race of player 1 - orcs then (подстраиваем инвентарь под стиль орков) else (подстраиваем под например альянс) |
01.08.2008, 23:09 | #9
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
akkolt ну дыг, так так и есть.......
ты меня недооцениваеш?) Jon добавил: akkolt просто вопрос выше стоял можно ли изменить вид инва на протижении игры повторю, можно добавить такую способность системе, но не нужно |
01.08.2008, 23:10 | #10
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
А воизбежание проблем в мультиплеере можно создать несколько таких областей для инвентарей со своими декорами, тоесть для 12 игроков нам нужно 12 областей.
akkolt добавил: Цитата:
Вовсе нет... скорее даже наоборот |
|
01.08.2008, 23:11 | #11
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
Цитата:
это так как реалиовано в инвентаре димонта или нетрата - ЭТО НЕПРАВЕЛЬНО и вообще прочитай статью а потом предожения говори там использует более совершеный метод инвентарь будет для всех играков на одной и тойже территории, на одной и тойже, но если они все 12 играков одновремено запустят инв то никаких проблем е будет |
|
01.08.2008, 23:12 | #12
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
krimatoriy ну я значит не так понял, ну вообщем все уже сказали, все делается само, единственое что нужно сделать, это дать юнитам абилки с помощью соответствующих функций
а также при инициилизации карты вызывать одну единтвеную функцю, в которую передаеш группу играков для которых создаеш нвентарь (фильтруется только для играков), позицию левого верхнего угла инва, ну и абилку которой инв вызывается, все остальное делается автоматичски |
01.08.2008, 23:15 | #13
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
akkolt
offline
Опыт:
13,826Активность: |
Цитата:
Интересно... сейчас почитаю. Цитата:
Ну дыг можно. По сути можно менять и во время игры (инвентарь) но этого лучше не делать. Так как интерфейс альянса с инвентарём нежитей будет выглядеть не тру. |
||
01.08.2008, 23:18 | #14
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Модельщик
Во славу JC!
offline
Опыт:
2,686Активность: |
А не проще вместо трекаблей юзать юнитов? |
02.08.2008, 14:39 | #15
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
Модельщик ты сам осознаш насколько это тупо и неэффективно или мне подробно обьяснить?
|
02.08.2008, 14:44 | #16
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Модельщик
Во славу JC!
offline
Опыт:
2,686Активность: |
Осазнаю, но это вариант, хоть и тупой)) |
02.08.2008, 14:45 | #17
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
J
expert
offline
Опыт:
48,447Активность: |
если бы там были юниты, ну вопервых самое первое что сразу бросается в глаза, ну никак нельзя определить наведение мыши на юнита, вот посмотри инвентарь что я приложил в вложения, и посмотри можно ли сделать так на юнитах, приличная часть функциональности инва лежит на этой способности
щелчок мыши сопровождается выделением юнита но даже если потом выделение сбрасывается происходит это не сразу и долю секунды видно что выделение пропало, это к списку косметических багов я не проверял но вроде радиус выбора мышкой юнита происходит в радиусе от его центра, т.е. еси я нажму на уголок кнбки то кнопка не нажмется, в случае трекаблов можно сделать ровную нажимающуюся кнопку, также косметический баг зачем мне описывать такие вариаенты в статье? также проще создавать каждый инвентарь для отдельного игрока, тобы они могли параллельно работать, но это тоже фигня можно вообще как некоторые - декорации (100x100) прям в редакторе ставить на землю и юнитов ставить, это вообще бред ламеров Jon добавил: К томуже способ что реализован в статье - _легче_, поему? он леге для того кто будет его юзать самое главное - Это не как можно проще сделать систему, а чтобы как можно проще применять ее если бы мы делали как я сказал выше, то чтобы применить ее надо было бы перенести все декорации раставить в том же порядке, расставить юнитов, тоже в нуном порядке, заменить каждого юнита в триггерах на невого которого поставили, добавление предмета была бы вообще геморной и в том случае как в статье - чтобы сделать инвентарь - всего _одна строчка кода_ и весь инвентарь готов, всего одна строка добавления к базе донных нового предмета статья не "инвентари делать легко, может любой дураг", а "делаем универсальный инвентарь" ну и она еще к томуже и не закончена, но главные идеи и концепции подает |
02.08.2008, 15:05 | #18
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
Модельщик
Во славу JC!
offline
Опыт:
2,686Активность: |
Я понел
Модельщик добавил: У меня идея: в редакторе есть возможность наносить на экран эффекты - это можно заюзать - заменить эффекты на внешность инва, а всё остальное оставить |
02.08.2008, 15:16 | #19
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|
ScorpioT1000
Работаем
offline
Опыт: отключен
|
Цитата:
TT напиши это в JNGP ScorpioT1000 добавил: Модельщик, это все брет. Jon статья мало кому пригодится ибо кто знает тот сам сделает) хотя 2-3 оптимальных идеи тут есть :j |
|
02.08.2008, 15:27 | #20
+0/−0
Профиль |
Приват |
Поиск |
IP: Записан
|