Добавлен , опубликован

Основы Интерфейса

Содержание:
К сожалению, в Warcraft 3 существует опасная ошибка с фреймами, которая может привести к сбою игры. Эта ошибка может произойти, когда пользовательская карта создает пользовательские UI-фреймы и карта сохраняется и загружается с использованием сохранения warcraft 3. После загрузки все такие созданные пользователем фреймы становятся сломанными, скрытыми и непригодными для использования. (примечание от Hate : все нативные функции связанные с фреймами не загружаются игрой в память, вследствие чего обращение к ним вызывает фатал. кратко - близзарды агонь) Если использовать такой сломанный фрейм, игра может вылететь. Это применимо с версии 1.31, первой версии с настраиваемым пользовательским интерфейсом, до PTR 1.32.9 (время, когда я это писал).
Можно обойти ошибку, отключив сохранение / загрузку или воссоздав все пользовательские фреймы при загрузке. Хотя это звучит сложно, для статических кадров сделать это довольно просто.
Как понял ваш автор, если сохранить карту с фреймами, а потом загрузить ее, то происходит краш. Причина в переменных фреймов. Сами по себе они являются локальными объектами, и наверное близзардами не рассчитывалось, что они будет переноситься из игры в игру. Вот были какие то записи в переменной фрейма, а после загрузки уже в этой переменной можно считать, что ничего нет. Handle сохранены, а вот сами фреймы сломаны (существуют ли они). Вызов нативных функции к этим фреймам с помощью переменных приводит к фаталу. Фреймы надо заново пересоздать в этих переменных, и настроить их (коорды, размеры, текстуру задать). Проще всю структуру переменных фреймов на глобалках хранить, чтобы случае чего, при загрузке загрузить все. Также еще и импорт с шаблонами надо снова прогружать
Мой вариант образца ошибки с созданием кнопки
--инициируем переменные
local gameUI  = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)

local button = BlzCreateFrameByType("BUTTON", "MyIconButton", gameUI , "ScoreScreenTabButtonTemplate", 0)
local buttonIcon = BlzCreateFrameByType("BACKDROP", "MyIconButtonIcon", button , "", 0)
local tooltip[a][b] = BlzCreateFrameByType("FRAME", "", button, "", 0)

BlzFrameSetAbsPoint(button, FRAMEPOINT_BOTTOMLEFT, x,y)
BlzFrameSetSize(button, size_button, size_button)
BlzFrameSetTexture(buttonIcon, "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn", 0, true)
BlzFrameSetTooltip(button,tooltip)
BlzFrameSetVisible(tooltip, false)

        	TimerStart(CreateTimer(),0.03,true,function()
                    if BlzFrameIsVisible(tooltip)  then --здесь уже будет ошибка после загрузки, тк после загрузки уже не записана никакая переменая. Вызов к tooltip приводит к вылету из игры.

                    end
            end
мой собственный вариант решения
На самом деле это мой краткий обзор. Тут не предлагается полное решение. Поскольку тут еще надо базу данных хранить. К счастью вы можете записать число, строку, boolean и др. Эти данные в таблицах не должны потеряться. Надо записать координаты, размер, текстура, скрыт/не скрыт и пр. Это большая работа. Не забудьте еще и через BlzLoadTOCFile заново прогрузить ток-файл

--инициируем переменные
function InitVarFrames()
local gameUI  = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)

local button = BlzCreateFrameByType("BUTTON", "MyIconButton", gameUI , "ScoreScreenTabButtonTemplate", 0)
local buttonIcon = BlzCreateFrameByType("BACKDROP", "MyIconButtonIcon", button , "", 0)
local tooltip[a][b] = BlzCreateFrameByType("FRAME", "", button, "", 0)

BlzFrameSetAbsPoint(button, FRAMEPOINT_BOTTOMLEFT, x,y)
BlzFrameSetSize(button, size_button, size_button)
BlzFrameSetTexture(buttonIcon, "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn", 0, true)
BlzFrameSetTooltip(button,tooltip)
BlzFrameSetVisible(tooltip, false)
end

        local triggerLoad = CreateTrigger()
        timer = CreateTimer()
        TriggerRegisterGameEvent(triggerLoad, EVENT_GAME_LOADED)
        TriggerAddAction(triggerLoad, function()
            TimerStart(timer, 0, false, function()
                InitVarFrames()
                --for _,v in ipairs(data) do v() end
            end)
        end)
Достаточно просто поместить все функции создания фреймов в массив и снова запустить эту вещь при загрузке карты.
Это будет такая система, которая работает, в которой можно регистрировать функции Framecreation.
lua код


-- in 1.31 and upto 1.32.9 PTR (when I wrote this). Frames are not correctly saved and loaded, breaking the game.
-- This runs all functions added to it with a 0s delay after the game was loaded.
do
    local data = {}
    local real = MarkGameStarted
    local timer
    
    function FrameLoaderAdd(func)
        table.insert(data, func)
    end
    function MarkGameStarted()
        real()
        local trigger = CreateTrigger()
        timer = CreateTimer()
        TriggerRegisterGameEvent(trigger, EVENT_GAME_LOADED)
        TriggerAddAction(trigger, function()
            TimerStart(timer, 0, false, function()
                for _,v in ipairs(data) do v() end
            end)
            
        end)
    end
end
И пример, который его использует.
lua код


-- HideMinDamageV3
do
    local realFunction = MarkGameStarted
    local timer, damageA, damageB, parentA, parentB, damageA2, damageB2, text, index
    local function update(sourceFrame, targetFrame)
        text = BlzFrameGetText(sourceFrame)
        index = string.find(text, " - ", 1, true)
        BlzFrameSetText(targetFrame, string.sub( text, index + 3))
    end
    local function Init()
        BlzLoadTOCFile("war3mapImported\\HideMinDamage.toc")
        if not timer then timer = CreateTimer() end
        damageA = BlzGetFrameByName("InfoPanelIconValue", 0)
        damageB = BlzGetFrameByName("InfoPanelIconValue", 1)
        parentA = BlzGetFrameByName("SimpleInfoPanelIconDamage",0)
        parentB = BlzGetFrameByName("SimpleInfoPanelIconDamage",1)
        BlzCreateSimpleFrame("CustomDamageString", parentA, 0)
        damageA2 = BlzGetFrameByName("CustomDamageStringValue", 0)
        BlzCreateSimpleFrame("CustomDamageString", parentB, 1)
        damageB2 = BlzGetFrameByName("CustomDamageStringValue", 1)
        BlzFrameSetFont(damageA, "", 0, 0)
        BlzFrameSetFont(damageB, "", 0, 0)
        
        TimerStart(timer, 0.05, true, function()
            if BlzFrameIsVisible(parentA) then
                update(damageA, damageA2)
            end
            if BlzFrameIsVisible(parentB) then
                update(damageB, damageB2)
            end
        end)
    end
    function MarkGameStarted()
        realFunction()
        realFunction = nil
        
        Init()
        if FrameLoaderAdd then FrameLoaderAdd(Init) end
    end
end
или тот же пример на jass
jass код
library HideMinDamageText initializer init_function requires FrameLoader
// HideMinDamageV3
    globals
        private framehandle DamageA
        private framehandle DamageB
        private framehandle DamageA2
        private framehandle DamageB2
        private framehandle ParentA
        private framehandle ParentB
        private string Text
        private integer Index
        private integer LoopA
        private integer LoopAEnd
    endglobals
    private function find takes nothing returns nothing
        set LoopAEnd = StringLength(Text) - 1
        set LoopA = 1
        loop
            exitwhen LoopA >= LoopAEnd
            if SubString(Text, LoopA, LoopA +3) == " - " then
                set Index = LoopA + 3
                return
            endif
            set LoopA = LoopA + 1
        endloop
        set Index = 0
    endfunction
    
    private function update takes nothing returns nothing
        if BlzFrameIsVisible(ParentA) then
            set Text = BlzFrameGetText(DamageA)
            call find()
            call BlzFrameSetText(DamageA2, SubString(Text, Index, StringLength(Text)))
        endif
        if BlzFrameIsVisible(ParentB) then
            set Text = BlzFrameGetText(DamageB)
            call find()
            call BlzFrameSetText(DamageB2, SubString(Text, Index, StringLength(Text)))
        endif
    endfunction
    private function At0s takes nothing returns nothing
        call BlzLoadTOCFile("war3mapImported\\HideMinDamage.toc")
        set ParentA = BlzGetFrameByName("SimpleInfoPanelIconDamage", 0)
        set ParentB = BlzGetFrameByName("SimpleInfoPanelIconDamage", 1)
        set DamageA = BlzGetFrameByName("InfoPanelIconValue", 0)
        set DamageB = BlzGetFrameByName("InfoPanelIconValue", 1)
        call BlzCreateSimpleFrame("CustomDamageString", ParentA, 0)
        set DamageA2 = BlzGetFrameByName("CustomDamageStringValue", 0)
        call BlzCreateSimpleFrame("CustomDamageString", ParentB, 1)
        set DamageB2 = BlzGetFrameByName("CustomDamageStringValue", 1)
        call BlzFrameSetFont(DamageA, "", 0, 0)
        call BlzFrameSetFont(DamageB, "", 0, 0)
        call TimerStart(GetExpiredTimer(), 0.05, true, function update)
    endfunction
    private function init_function takes nothing returns nothing
        call FrameLoaderAdd(function At0s)
        call TimerStart(CreateTimer(), 0, false, function At0s)
    endfunction
endlibrary
jass код


library FrameLoader initializer init_function
// in 1.31 and upto 1.32.9 PTR (when I wrote this). Frames are not correctly saved and loaded, breaking the game.
// This library runs all functions added to it with a 0s delay after the game was loaded.
// function FrameLoaderAdd takes code func returns nothing
    // func runs when the game is loaded.
    globals
        private trigger eventTrigger = CreateTrigger()
        private trigger actionTrigger = CreateTrigger()
        private timer t = CreateTimer()
    endglobals
    function FrameLoaderAdd takes code func returns nothing
        call TriggerAddAction(actionTrigger, func)
    endfunction

    private function timerAction takes nothing returns nothing
        call TriggerExecute(actionTrigger)
    endfunction
    private function eventAction takes nothing returns nothing
        call TimerStart(t, 0, false, function timerAction)
    endfunction
    private function init_function takes nothing returns nothing
        call TriggerRegisterGameEvent(eventTrigger, EVENT_GAME_LOADED)
        call TriggerAddAction(eventTrigger, function eventAction)        
    endfunction
endlibrary

Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
1
32
2 года назад
Отредактирован Берги
1
Да способ ужасный, но дело больше не в том, что нет фреймов при загрузке а в том что нет загрузка сама по себе выдаёт фатал, мне кажется до события EVENT_GAME_LOADED даже дело не дойдёт, легче вообще сделать собственную систему загрузки, чем это починить, + тут только для статик фреймов, с которыми даже в взаимодействовать нельзя. В любом случае хорошо, что хоть какое-то решение известно.
Вообще эта тема требует отдельного изучения и индивидуального подхода для каждой карты. Этот код вообще вырван из контекста, что за функции real() и v()?, выглядит как нерабочий огрызок. Но я понял, что это лишь пример, спасибо за материал (Я вообще вот жил и не знал даже что существует EVENT_GAME_LOADED)
0
27
2 года назад
Отредактирован MpW
0
спасибо за исправление/дополнение. я еще не тестил. но думал, хорошо бы сделать. а когда буду когда-нибудь работать, может закину сюда попозже
Чтобы оставить комментарий, пожалуйста, войдите на сайт.