Добавлен MpW,
опубликован
Основы Интерфейса
Содержание:
К сожалению, в 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
Содержание
`
ОЖИДАНИЕ РЕКЛАМЫ...
Чтобы оставить комментарий, пожалуйста, войдите на сайт.
Отредактирован Берги
Вообще эта тема требует отдельного изучения и индивидуального подхода для каждой карты. Этот код вообще вырван из контекста, что за функции real() и v()?, выглядит как нерабочий огрызок. Но я понял, что это лишь пример, спасибо за материал (Я вообще вот жил и не знал даже что существует EVENT_GAME_LOADED)
Отредактирован MpW