Способ реализации:
Версия Warcraft:

Moveable Frame – система для перемещения фреймов мышью

Что это?
Moveable Frame – это система, которая позволяет "хватать" и перемещать фреймы интерфейса вслед за курсором мыши.
В чём проблема?
Игра не предоставляет прямого доступа к экранным координатам мыши – только к игровым (те, что на карте). Без точного позиционирования курсора на экране плавное перемещение фреймов невозможно.
Решение:
Используются невидимые "пустые" фреймы размером 0.01x0.01, покрывающие весь экран сеткой. Когда мышь движется, система определяет, над каким из этих микро-фреймов находится курсор, и обновляет позицию перетаскиваемого фрейма.
Используются система мировых координат World2Screen, которая работает гораздо лучше сетки
World2Screen
library World2Screen
globals
real ScreenX = 0
real ScreenY = 0
endglobals
function World2Screen takes real x, real y, real z returns nothing
local real angleOfAttack = -GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK)
local real fieldOfView = GetCameraField(CAMERA_FIELD_FIELD_OF_VIEW)
local real rotation = GetCameraField(CAMERA_FIELD_ROTATION) + bj_PI
local real cosAttack = Cos(angleOfAttack)
local real sinAttack = Sin(angleOfAttack)
local real cosRot = Cos(rotation)
local real sinRot = Sin(rotation)
local real M11 = cosAttack*cosRot
local real M12 = cosAttack*sinRot
local real M13 = sinAttack
local real M21 = -sinRot
local real M22 = cosRot
local real M23 = 0
local real M31 = -cosRot*sinAttack
local real M32 = -sinAttack*sinRot
local real M33 = cosAttack
local real dx = x - GetCameraEyePositionX()
local real dy = y - GetCameraEyePositionY()
local real dz = z - GetCameraEyePositionZ()
local real xPrime = M11*dx + M12*dy + M13*dz
local real yPrime = M21*dx + M22*dy + M23*dz
local real zPrime = M31*dx + M32*dy + M33*dz
local real scaling = 1.04 / ( 2 * Tan(fieldOfView / 2) )
set ScreenX = 0.4 - (scaling * yPrime) / xPrime
set ScreenY = 0.355 - (scaling * zPrime) / xPrime
endfunction
endlibrary

Из нового в переписанной версии с Lua на vJass:

  • Расширенные границы – фреймы можно перемещать *за пределы стандартной зоны 0.0–0.8*.
Проблема с оптимизацией решена
Система создает 12,921 handle-ов. На пустой карте проблем нет, но при заполненной карте предполагаю возможны лаги.
  • Теперь система создает всего 106 хэндлов
Невозможно сделать движение фрейма с мышкой 1к1, потому что событие Mouse Move срабатывает примерно не чаще чем раз в 0.15 сек
✅ Два режима перемещения:
🔘 Удержание ПКМ – фрейм движется, пока зажата правая кнопка мыши.
🔘 Переключение по клику – первый клик ПКМ активирует движение, второй – сбрасывает.
✅ Плавное завершение анимации – фрейм теперь всегда доходит до конечной точки, а не останавливается раньше.
✅ Автоматический сброс масштаба – после завершения движения фрейм возвращается к стандартному размеру (scale = 1.0).
То же самое видео, на случай, если видео выше не грузится

Скачать карту:


FrameGrid (Больше не используется)
library FrameGrid  requires MoveAbleFrame


struct structFrame
    framehandle array   GridFrames[4800]
    framehandle array   GridFrames2[4800]
    integer             LastFrame = 0
    timer               Timer  
    real                FramesEachCol
    hashtable Coords 
    integer             index = 0
    
    method setFrameCoords takes framehandle frame, real x, real y returns nothing
        call SaveReal(Coords, GetHandleId(frame), 0, x)
        call SaveReal(Coords, GetHandleId(frame), 1, y)
    endmethod
    
    method GetFrameCoordsX takes framehandle frame returns real
        return LoadReal(Coords, GetHandleId(frame), 0)
    endmethod
    
    method GetFrameCoordsY takes framehandle frame returns real
        return LoadReal(Coords, GetHandleId(frame), 1)
    endmethod

endstruct

globals
    structFrame FrameGrid
    framehandle         Boss = null
endglobals

/*function updateSimple takes nothing returns nothing
local framehandle value
local integer index = 0
    if BlzFrameIsVisible(FrameGrid.Boss) then
    call BJDebugMsg("updateSimple BlzFrameIsVisible")
        loop
            exitwhen index == FrameGrid.LastFrame + 1
            set value = FrameGrid.GridFrames2[index]
            if BlzFrameIsVisible(value) then
                call moveFrame(FrameGrid.GetFrameCoordsX(FrameGrid.GridFrames2[index]),FrameGrid.GetFrameCoordsY(FrameGrid.GridFrames2[index]), true)
                return
            endif
            set index = index + 1
        endloop
    endif
endfunction*/


function update2 takes nothing returns nothing
    local integer yA = FrameGrid.LastFrame 
    local integer yB = FrameGrid.LastFrame
    
    if BlzFrameIsVisible(Boss) then
        loop
        exitwhen (yA < 0 and yB >= 4800)
            if yA >= 0 then
                if BlzFrameIsVisible(FrameGrid.GridFrames2[yA]) then
                    set FrameGrid.LastFrame = yA
                    call BJDebugMsg("update: trying to move " + BlzFrameGetName(FrameGrid.GridFrames2[yA]))
                    call moveFrame(FrameGrid.GetFrameCoordsX(FrameGrid.GridFrames2[yA]),FrameGrid.GetFrameCoordsY(FrameGrid.GridFrames2[yA]), true)
                    return
                endif
                set yA = yA - 1
            endif
            if BlzFrameIsVisible(FrameGrid.GridFrames2[yB]) then
                set FrameGrid.LastFrame = yB
                call BJDebugMsg("update: trying to move" + BlzFrameGetName(FrameGrid.GridFrames2[yB]))
                call moveFrame(FrameGrid.GetFrameCoordsX(FrameGrid.GridFrames2[yB]),FrameGrid.GetFrameCoordsY(FrameGrid.GridFrames2[yB]), true)
                return
            else
                set yB = yB + 1
            endif
        endloop
    endif
endfunction

function HideBossFrame takes nothing returns nothing
    call BlzFrameSetVisible(Boss, false)
endfunction

globals
    framehandle newButton
    framehandle tooltipButton
endglobals

function onGameStart2 takes nothing returns nothing
    local real xSize = 0.01
    local real ySize = 0.01
    local real yStart = ySize/2
    local real xStart = xSize/2
    local real x = -0.134
    local real y
    call PolledWait2(0.01)
    set FrameGrid = structFrame.create()
    set FrameGrid.Coords = InitHashtable()
    set FrameGrid.Timer = CreateTimer()
    call TimerStart(FrameGrid.Timer, 0.01, true, function update2)
    set Boss = BlzCreateFrameByType("BUTTON", "FrameGridBoss", BlzGetFrameByName("ConsoleUIBackdrop", 0),"",0)
    set FrameGrid.FramesEachCol = 0.6 / ySize

    loop
    exitwhen x>0.932
        set y = yStart
        loop
        exitwhen y>=0.6
            set newButton = BlzCreateFrameByType("FRAME", "FRAME" + I2S(FrameGrid.index), Boss,"",0)
            set tooltipButton = BlzCreateFrameByType("FRAME", "FRAME" + I2S(FrameGrid.index), Boss,"",0)
            call BlzFrameSetAbsPoint(newButton, FRAMEPOINT_CENTER, x, y)
            call BlzFrameSetSize(newButton, xSize, ySize)
            call BlzFrameSetTooltip(newButton, tooltipButton)
            call BlzFrameSetEnable(newButton, false)
            call BlzFrameSetEnable(tooltipButton, false)
            
            call FrameGrid.setFrameCoords(newButton,x,y)
            call FrameGrid.setFrameCoords(tooltipButton,x,y)
            set FrameGrid.GridFrames2[FrameGrid.index] = tooltipButton

            set FrameGrid.GridFrames[FrameGrid.index] = newButton
            
            set FrameGrid.index = FrameGrid.index + 1

        set y = y + ySize
        endloop
    set x = x + xSize
    endloop
    call BlzFrameSetVisible(Boss, false)
call onGameStart()
endfunction

endlibrary

CheckBox Setting
globals

    
    //      === Включаем перемещение предметов инвентаря только если зажата ПКМ ===
    boolean RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE = false
    
    //      === Таймер утечный(хотя раньше мне казалось это не вызывало утечек), но доводит фрейм до мышки, если было сильное дерганье или мышка переместилась быстро ===
//        === При true код будет вызывать десинхронизацию, а значит это для одиночной игры ===
    boolean DRAG_TIMER_ENABLE = false
    
    
    constant string DEFAULT_ICON = "ReplaceableTextures\\CommandButtons\\BTNSelectHeroOn"
    constant string BACKGROUND_UNITS = "war3mapImported\\InventoryTexture5Human.tga"
    constant string BACKGROUND_SLOTS = "war3mapImported\\InventoryTexture4.tga"   
    
    
    
endglobals

function CheckBoxDRAG_ENABLE takes nothing returns nothing
    if BlzGetTriggerFrameEvent() == FRAMEEVENT_CHECKBOX_CHECKED then
        set RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE = true
    else
        set RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE = false
    endif
endfunction

function CheckBoxDRAG_ENABLE_TIMER takes nothing returns nothing
    if BlzGetTriggerFrameEvent() == FRAMEEVENT_CHECKBOX_CHECKED then
        set DRAG_TIMER_ENABLE = true
    else
        set DRAG_TIMER_ENABLE = false
    endif
endfunction

function CheckBoxInit takes nothing returns nothing
    local framehandle parent = BlzGetFrameByName("ConsoleUIBackdrop", 0)//BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)
    local framehandle checkBox = BlzCreateFrame("QuestCheckBox2", parent, 0, 0)
    local framehandle text = BlzCreateFrameByType("TEXT", "", checkBox, "StandardTextTemplate", 0)
    
    local framehandle checkBox2 = BlzCreateFrame("QuestCheckBox2", parent, 0, 0)
    local framehandle text2 = BlzCreateFrameByType("TEXT", "", checkBox2, "StandardTextTemplate", 0)
    
    local trigger t = CreateTrigger()
    local trigger t2 = CreateTrigger()
    
    // Основные настройки чекбокса
    call BlzFrameSetPoint(checkBox, FRAMEPOINT_LEFT, parent, FRAMEPOINT_LEFT, 0.01, 0.3 )
    call BlzFrameSetSize(checkBox, 0.03, 0.03)

    // Настройки текста
    call BlzFrameSetText(text, "Включить режим, |nдвижения только при зажатой правой кнопке")
    call BlzFrameSetPoint(text, FRAMEPOINT_LEFT, checkBox, FRAMEPOINT_RIGHT, 0.01, 0)
    call BlzFrameSetScale(text, 1.2)
    call BlzFrameSetTextAlignment(text, TEXT_JUSTIFY_LEFT, TEXT_JUSTIFY_LEFT) // Выравнивание
    
    
    // Регистрация событий
    call BlzTriggerRegisterFrameEvent(t, checkBox, FRAMEEVENT_CHECKBOX_CHECKED)
    call BlzTriggerRegisterFrameEvent(t, checkBox, FRAMEEVENT_CHECKBOX_UNCHECKED)
    call TriggerAddAction(t, function CheckBoxDRAG_ENABLE)

    call BlzFrameSetPoint(checkBox2, FRAMEPOINT_LEFT, parent, FRAMEPOINT_LEFT, 0.01, 0.3 - 0.05)
    call BlzFrameSetSize(checkBox2, 0.03, 0.03)
    
    call BlzFrameSetText(text2, "Включить таймер, |nдля продолжения движения после клика/отпускания ПКМ")
    call BlzFrameSetPoint(text2, FRAMEPOINT_LEFT, checkBox2, FRAMEPOINT_RIGHT, 0.01, 0)
    call BlzFrameSetScale(text2, 1.2)
    call BlzFrameSetTextAlignment(text2, TEXT_JUSTIFY_LEFT, TEXT_JUSTIFY_LEFT)
    
    call BlzFrameSetLevel(text, 0)
    call BlzFrameSetLevel(text2, 0)
    call BlzTriggerRegisterFrameEvent(t2, checkBox2, FRAMEEVENT_CHECKBOX_CHECKED)
    call BlzTriggerRegisterFrameEvent(t2, checkBox2, FRAMEEVENT_CHECKBOX_UNCHECKED)
    call TriggerAddAction(t2, function CheckBoxDRAG_ENABLE_TIMER)
endfunction
MoveAbleFrame (Основной)
library MoveAbleFrame initializer onTriggerInit requires World2Screen

globals
    real targetX = 0.0
    real targetY = 0.0
    real currentX = 0.0
    real currentY = 0.0
    real moveSpeed = 0.01 // от 0 до 1: выше — быстрее, ниже — плавнее
    real prevTargetX = 0.0
    real prevTargetY = 0.0
    real deltaX = 0.0
    real deltaY = 0.0
    boolean wasMoving // Флаг для того, чтоб сделать фрейм размером 1 после догонки
    boolean PolledMoving = false// Флаг для того, чтоб сделать фрейм размером 1 после догонки
endglobals

struct structMoveAbleFrame
    boolean         Enabled = true
    
    framehandle     Frame = null
    framehandle     PlayerHoveredFrame
    framepointtype  FramePoint
    trigger         TriggerFrameEnter
    triggeraction   TriggerFrameEnterAction
    trigger         TriggerFrameLeave
    triggeraction   TriggerFrameLeaveAction
    trigger         MouseClickTrigger
    triggeraction   MouseClickTriggerAction
    trigger         MouseReleaseTrigger
    triggeraction   MouseReleaseTriggerAction
    trigger         MOUSE_MOVE_Trigger
    triggeraction   MOUSE_MOVE_TriggerAction
endstruct

globals
    structMoveAbleFrame MoveAbleFrame
endglobals

function enable takes player p, boolean flag returns boolean
    if GetLocalPlayer() == p then
        if flag == null then
            set MoveAbleFrame.Enabled = not MoveAbleFrame.Enabled
        else
            set MoveAbleFrame.Enabled = flag
        endif
    endif
    return MoveAbleFrame.Enabled
endfunction

function setup takes framehandle frame returns nothing
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameEnter, frame, FRAMEEVENT_MOUSE_ENTER)
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameLeave , frame, FRAMEEVENT_MOUSE_LEAVE)
endfunction


function startMoving takes framehandle frame, framepointtype framePoint returns nothing
    set MoveAbleFrame.Frame = frame
    if framePoint == null then
       set framePoint = FRAMEPOINT_CENTER
    endif
    set MoveAbleFrame.FramePoint = framePoint
endfunction


function moveFrame takes real x, real y returns nothing
    call BlzFrameClearAllPoints(MoveAbleFrame.Frame)
    call BlzFrameSetAbsPoint(MoveAbleFrame.Frame, MoveAbleFrame.FramePoint, x, y)
endfunction


function GetTriggerPlayerHoveredFrame takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = BlzGetTriggerFrame()
    endif
endfunction

function GetTriggerPlayerHoveredFrameNull takes nothing returns nothing
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = null
    endif
endfunction

globals
    location moveLoc = Location(0, 0)
    boolean flag = false
endglobals

function TempFrameSetting takes nothing returns nothing
    call BlzFrameSetEnable(MoveAbleFrame.PlayerHoveredFrame, false)
    call BlzFrameSetLevel(MoveAbleFrame.PlayerHoveredFrame, 9)
    call BlzFrameSetScale(MoveAbleFrame.PlayerHoveredFrame, 0.8)
endfunction

function NormalFrameSetting takes nothing returns nothing
    call BlzFrameSetScale(MoveAbleFrame.Frame, 1.0)
    call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
    call BlzFrameSetLevel(MoveAbleFrame.Frame, 6)
    set MoveAbleFrame.Frame = null
endfunction


globals
    real mouseX = 0.0
    real mouseY = 0.0
    real mouseX_prev = 0.0
    real mouseY_prev = 0.0
    real mouseX_interp = 0.0
    real mouseY_interp = 0.0
    real interpProgress = 1.0
    real interpSpeed  = 0
endglobals

function UpdateTargetXYInstant takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z

    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)
    call World2Screen(x, y, z)

    // не трогаем prev/interp — сразу задаём target
    set targetX = ScreenX
    set targetY = ScreenY
endfunction
function UpdateMouseScreenCoords takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z

    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)
    call World2Screen(x, y, z)

    set mouseX_prev = mouseX
    set mouseY_prev = mouseY
    set mouseX = ScreenX
    set mouseY = ScreenY
    set mouseX_interp = mouseX
    set mouseY_interp = mouseY
    set interpProgress = 1.0

    set targetX = mouseX
    set targetY = mouseY
endfunction

function PolledWait2 takes real time returns nothing
local timer t
local real R
    if time>0. then
        set t = CreateTimer()
        call TimerStart(t,time,false,null)
        loop
            set R=TimerGetRemaining(t)
            exitwhen R<=0.
                call TriggerSleepAction(R)
        endloop
        call DestroyTimer(t)
        set t=null
    endif 
endfunction

function GetWorld2Screen takes nothing returns real
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z
    
    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)
    call World2Screen(x, y, z)
    return SquareRoot((currentX - ScreenX)*(currentX - ScreenX) + (currentY - ScreenY)*(currentY - ScreenY))
endfunction

function MouseClick takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local real dist
    local timer t
    if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
        if MoveAbleFrame.Enabled and GetLocalPlayer() == p then
            if RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE then
                // Стандартное поведение (перетаскивание)
                if MoveAbleFrame.PlayerHoveredFrame != null then
                    set flag = true
                    call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
                    call TempFrameSetting()
                    call UpdateMouseScreenCoords() 
                    set currentX = targetX
                    set currentY = targetY
                    call moveFrame(currentX, currentY)
                else
                    set flag = false
                    set MoveAbleFrame.Frame = null
                endif
            else
                // Альтернативное поведение (вкл/выкл)
                if MoveAbleFrame.PlayerHoveredFrame != null then
                    set flag = not flag
                    set PolledMoving = not PolledMoving
                    if flag then
                        call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
                        call UpdateMouseScreenCoords()
                        set currentX = targetX
                        set currentY = targetY
                        call moveFrame(currentX, currentY)
                        call TempFrameSetting()
                    else
                        // Добавлено: сброс масштаба при flag = false (клик по фрейму)
                        call NormalFrameSetting()
                    endif
                else
                    // Клик вне фрейма — сброс (если PolledMoving = true)
                    if DRAG_TIMER_ENABLE then
                        if PolledMoving then
                            set PolledMoving = not PolledMoving
                            if GetWorld2Screen() > 0.0029 then
                                set t = CreateTimer()
                                call TimerStart(t, 0.15, false, null)
                                loop
                                    set dist = GetWorld2Screen()
                                    exitwhen dist < 0.002 or TimerGetRemaining(t) <= 0.
                                    call TriggerSleepAction(0.01)
                                endloop
                                call DestroyTimer(t)
                                set t = null
                            endif
                        endif
                    endif
                    set flag = false
                    call NormalFrameSetting()
                endif 
            endif
        endif
    endif
endfunction

function MouseRelease takes nothing returns nothing
    local real dist
    if MoveAbleFrame.Frame == null then
        return // нет фрейма — ничего не двигаем
    endif
    if RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE then
        set dist = GetWorld2Screen()

        if dist < 0.003 then
            // Мышь уже почти на фрейме — завершаем немедленно
            set flag = false
            set wasMoving = true
            set targetX = ScreenX
            set targetY = ScreenY
        else
            if DRAG_TIMER_ENABLE then
                 // Мышь далеко — ждём, пока дотянется
                call PolledWait2(0.15)
            endif
            set flag = false
            set wasMoving = true
            call UpdateTargetXYInstant()
        endif 
    endif
endfunction

function OnMouseMove takes nothing returns nothing
    local real newX = BlzGetTriggerPlayerMouseX()
    local real newY = BlzGetTriggerPlayerMouseY()
    //set GetFunctionName_DEBUG = "OnMouseMove"
    set mouseX_prev = mouseX
    set mouseY_prev = mouseY

    set mouseX = newX
    set mouseY = newY
    set interpProgress = 0.0
endfunction

function update2 takes nothing returns nothing
    local real dx
    local real dy
    local real distMouse
    local real dist
    local real dynamicSpeed
    local real speed = moveSpeed
    if MoveAbleFrame.Frame == null then
        return // нет фрейма — ничего не двигаем
    endif
    if flag or wasMoving then
        set dx = mouseX - mouseX_prev
        set dy = mouseY - mouseY_prev
        set distMouse = SquareRoot(dx * dx + dy * dy)

        set interpSpeed = 0.1 + distMouse * 0.6
        if interpSpeed > 0.3 then
            set interpSpeed = 0.3 // Ограничение скорости интерполяции
        endif
        set interpProgress = interpProgress + interpSpeed
        if interpProgress > 1.0 then
            set interpProgress = 1.0
        endif

        set mouseX_interp = mouseX_prev + (mouseX - mouseX_prev) * interpProgress
        set mouseY_interp = mouseY_prev + (mouseY - mouseY_prev) * interpProgress

        set targetX = mouseX_interp
        set targetY = mouseY_interp

        //set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
        //set dynamicSpeed = moveSpeed + dist * 0.4
        //if dynamicSpeed > 0.08 then
            //set dynamicSpeed = 0.08
        //endif

        //set currentX = currentX + (targetX - currentX) * dynamicSpeed
        //set currentY = currentY + (targetY - currentY) * dynamicSpeed
        set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
        if dist < 0.05 then
            set speed = moveSpeed + (0.05 - dist) * 2.0
            if speed > 0.025 then
                set speed = 0.025 // максимум, чтобы не прыгало
            endif
        endif

        set currentX = currentX * (1.0 - speed) + targetX * speed
        set currentY = currentY * (1.0 - speed) + targetY * speed

        call moveFrame(currentX, currentY)
        

        // завершение
        if not flag and dist < 0.001 then
            set wasMoving = false
            call BlzFrameSetScale(MoveAbleFrame.Frame, 1.0)
            call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
            call BlzFrameSetLevel(MoveAbleFrame.Frame, 6)
        endif
    endif
endfunction


function CoordsMouse takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z
    if flag == true then
        call MoveLocation(moveLoc, x, y)
        set z = GetLocationZ(moveLoc)
        call World2Screen(x,y,z)

        set mouseX_prev = mouseX
        set mouseY_prev = mouseY
        set mouseX = ScreenX
        set mouseY = ScreenY
        set interpProgress = 0.0
    endif
endfunction

function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = CreateTimer()
local timer Timer2 = CreateTimer()
    call PolledWait2(0.01)
    set MoveAbleFrame = structMoveAbleFrame.create()
    
    set MoveAbleFrame.TriggerFrameEnter = CreateTrigger()
    set MoveAbleFrame.TriggerFrameEnterAction = TriggerAddAction(MoveAbleFrame.TriggerFrameEnter,function GetTriggerPlayerHoveredFrame)
    
    set MoveAbleFrame.TriggerFrameLeave = CreateTrigger()
    set MoveAbleFrame.TriggerFrameLeaveAction = TriggerAddAction(MoveAbleFrame.TriggerFrameLeave, function GetTriggerPlayerHoveredFrameNull)

    set MoveAbleFrame.MouseClickTrigger = CreateTrigger()
    set MoveAbleFrame.MouseClickTriggerAction = TriggerAddAction(MoveAbleFrame.MouseClickTrigger, function MouseClick)
    
    set MoveAbleFrame.MouseReleaseTrigger = CreateTrigger()
    set MoveAbleFrame.MouseReleaseTriggerAction = TriggerAddAction(MoveAbleFrame.MouseReleaseTrigger, function MouseRelease)
    
    set MoveAbleFrame.MOUSE_MOVE_Trigger = CreateTrigger()
    set MoveAbleFrame.MOUSE_MOVE_TriggerAction = TriggerAddAction(MoveAbleFrame.MOUSE_MOVE_Trigger, function CoordsMouse)

    loop
    exitwhen playerIndex == GetBJMaxPlayers()
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseClickTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_DOWN)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseReleaseTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_UP)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MOUSE_MOVE_Trigger, Player(playerIndex), EVENT_PLAYER_MOUSE_MOVE)
        set playerIndex = playerIndex + 1
    endloop
call ExecuteFunc("Init")
call TimerStart(Timer, 0.001, true, function update2)
call ExecuteFunc("CheckBoxInit")
endfunction

endlibrary

MoveAbleFrame 2 (Второй вариант)
library MoveAbleFrame initializer onTriggerInit requires World2Screen

globals
    real targetX = 0.0
    real targetY = 0.0
    real currentX = 0.0
    real currentY = 0.0
    real moveSpeed = 0.04 // от 0 до 1: выше — быстрее, ниже — плавнее
    real prevTargetX = 0.0
    real prevTargetY = 0.0
    real deltaX = 0.0
    real deltaY = 0.0
endglobals

struct structMoveAbleFrame
    boolean         Enabled = true
    
    framehandle     Frame = null
    framehandle     PlayerHoveredFrame
    framepointtype  FramePoint
    trigger         TriggerFrameEnter
    triggeraction   TriggerFrameEnterAction
    trigger         TriggerFrameLeave
    triggeraction   TriggerFrameLeaveAction
    trigger         MouseClickTrigger
    triggeraction   MouseClickTriggerAction
    trigger         MouseReleaseTrigger
    triggeraction   MouseReleaseTriggerAction
    trigger         MOUSE_MOVE_Trigger
    triggeraction   MOUSE_MOVE_TriggerAction
endstruct

globals
    structMoveAbleFrame MoveAbleFrame
endglobals

function enable takes player p, boolean flag returns boolean
    if GetLocalPlayer() == p then
        if flag == null then
            set MoveAbleFrame.Enabled = not MoveAbleFrame.Enabled
        else
            set MoveAbleFrame.Enabled = flag
        endif
    endif
    return MoveAbleFrame.Enabled
endfunction

function setup takes framehandle frame returns nothing
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameEnter, frame, FRAMEEVENT_MOUSE_ENTER)
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameLeave , frame, FRAMEEVENT_MOUSE_LEAVE)
endfunction


function startMoving takes framehandle frame, framepointtype framePoint returns nothing
    set MoveAbleFrame.Frame = frame
    if framePoint == null then
       set framePoint = FRAMEPOINT_CENTER
    endif
    set MoveAbleFrame.FramePoint = framePoint
endfunction


function moveFrame takes real x, real y returns nothing
    call BJDebugMsg("moveFrame: called x=" + R2S(x) + " y=" + R2S(y))
    call BlzFrameClearAllPoints(MoveAbleFrame.Frame)
    call BlzFrameSetAbsPoint(MoveAbleFrame.Frame, MoveAbleFrame.FramePoint, x, y)
endfunction



function GetTriggerPlayerHoveredFrame takes nothing returns nothing
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = BlzGetTriggerFrame()
    endif
endfunction

function GetTriggerPlayerHoveredFrameNull takes nothing returns nothing
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = null
    endif
endfunction

globals
    location moveLoc = Location(0, 0)
    boolean flag = false
endglobals

function MouseClick takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z

    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)

    call BJDebugMsg("MouseClick")

    if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
        if MoveAbleFrame.PlayerHoveredFrame != null then
            call BJDebugMsg("MouseClick: frame is not null: " + BlzFrameGetName(MoveAbleFrame.PlayerHoveredFrame))
            if MoveAbleFrame.Enabled then
                if GetLocalPlayer() == p then
                    set flag = true // Перемещение только если действительно кликнули по фрейму
                    call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
                    call BlzFrameSetEnable(MoveAbleFrame.PlayerHoveredFrame, false)
                    call World2Screen(x,y,z)
                endif
            endif
        else
            call BJDebugMsg("MouseClick: PlayerHoveredFrame is null")
        endif
    endif
endfunction

function MouseRelease takes nothing returns nothing
call BJDebugMsg("MouseRelease")
set flag = false
    if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
        if MoveAbleFrame.Frame != null then
            call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
        endif
    endif
endfunction

function PolledWait2 takes real time returns nothing
local timer t
local real R
    if time>0. then
        set t = CreateTimer()
        call TimerStart(t,time,false,null)
        loop
            set R=TimerGetRemaining(t)
            exitwhen R<=0.
                call TriggerSleepAction(R)
        endloop
        call DestroyTimer(t)
        set t=null
    endif 
endfunction



function update2 takes nothing returns nothing
    local real dist
    local real dynamicSpeed
    if flag == true then
        set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
        set dynamicSpeed = moveSpeed + dist * 0.4
        if dynamicSpeed > 0.08 then
            set dynamicSpeed = 0.08
        endif
        // Плавное приближение к целевой позиции
        set currentX = currentX + (targetX - currentX) * moveSpeed
        set currentY = currentY + (targetY - currentY) * moveSpeed

        call moveFrame(currentX, currentY)
    endif
endfunction

function CoordsMouse takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real speedLimit = 0.25
    local real deltaLength = SquareRoot(deltaX * deltaX + deltaY * deltaY)
    local real z
    if flag == true then
        call MoveLocation(moveLoc, x, y)
        set z = GetLocationZ(moveLoc)
        call World2Screen(x,y,z)
        set deltaX = ScreenX - prevTargetX
        set deltaY = ScreenY - prevTargetY
        if deltaLength > speedLimit then
            set deltaX = deltaX * speedLimit / deltaLength
            set deltaY = deltaY * speedLimit / deltaLength
        endif
        set targetX = ScreenX + deltaX * 1.4
        set targetY = ScreenY + deltaY * 1.4

        set prevTargetX = ScreenX
        set prevTargetY = ScreenY
    endif
endfunction

function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = CreateTimer()
local timer Timer2 = CreateTimer()
    call PolledWait2(0.01)
    set MoveAbleFrame = structMoveAbleFrame.create()
    
    set MoveAbleFrame.TriggerFrameEnter = CreateTrigger()
    set MoveAbleFrame.TriggerFrameEnterAction = TriggerAddAction(MoveAbleFrame.TriggerFrameEnter,function GetTriggerPlayerHoveredFrame)
    
    set MoveAbleFrame.TriggerFrameLeave = CreateTrigger()
    set MoveAbleFrame.TriggerFrameLeaveAction = TriggerAddAction(MoveAbleFrame.TriggerFrameLeave, function GetTriggerPlayerHoveredFrameNull)

    set MoveAbleFrame.MouseClickTrigger = CreateTrigger()
    set MoveAbleFrame.MouseClickTriggerAction = TriggerAddAction(MoveAbleFrame.MouseClickTrigger, function MouseClick)
    
    set MoveAbleFrame.MouseReleaseTrigger = CreateTrigger()
    set MoveAbleFrame.MouseReleaseTriggerAction = TriggerAddAction(MoveAbleFrame.MouseReleaseTrigger, function MouseRelease)
    
    set MoveAbleFrame.MOUSE_MOVE_Trigger = CreateTrigger()
    set MoveAbleFrame.MOUSE_MOVE_TriggerAction = TriggerAddAction(MoveAbleFrame.MOUSE_MOVE_Trigger, function CoordsMouse)

    loop
    exitwhen playerIndex == GetBJMaxPlayers()
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseClickTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_DOWN)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseReleaseTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_UP)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MOUSE_MOVE_Trigger, Player(playerIndex), EVENT_PLAYER_MOUSE_MOVE)
        set playerIndex = playerIndex + 1
    endloop
call ExecuteFunc("onGameStart")
call TimerStart(Timer, 0.003, true, function update2)
endfunction

endlibrary

Код для тестирования системы
library Testb

private function click takes nothing returns nothing
    call BJDebugMsg("click " + BlzFrameGetName(BlzGetTriggerFrame()))
endfunction

private function enter takes nothing returns nothing
   // call BJDebugMsg("enter " + BlzFrameGetName(BlzGetTriggerFrame()))
endfunction

function onGameStart takes nothing returns nothing
    local framehandle gameUI = BlzGetFrameByName("ConsoleUIBackdrop", 0)
    local trigger t = CreateTrigger()
    local trigger t2 = CreateTrigger()
    local framehandle b
    call TriggerAddAction(t, function click)
    
    call TriggerAddAction(t2, function enter)
    
    set b =  BlzCreateFrameByType("GLUETEXTBUTTON", "b 1", gameUI, "ScriptDialogButton", 0)
    call BlzFrameSetAbsPoint(b, FRAMEPOINT_CENTER, 0.55, 0.4)
    call BlzFrameSetText(b, "b 1")

    call setup(b)

    call BlzTriggerRegisterFrameEvent(t, b, FRAMEEVENT_CONTROL_CLICK)
    call BlzTriggerRegisterFrameEvent(t2, b, FRAMEEVENT_MOUSE_ENTER)

    set b =  BlzCreateFrameByType("GLUETEXTBUTTON", "b 2", gameUI, "ScriptDialogButton", 0)
    call BlzFrameSetAbsPoint(b, FRAMEPOINT_CENTER, 0.55, 0.35)
    call BlzFrameSetText(b, "b 2")
    call setup(b)
    call BlzTriggerRegisterFrameEvent(t, b, FRAMEEVENT_CONTROL_CLICK)
    call BlzTriggerRegisterFrameEvent(t2, b, FRAMEEVENT_MOUSE_ENTER)
    set b =  BlzCreateFrameByType("GLUETEXTBUTTON", "b 3", gameUI, "ScriptDialogButton", 0)
    call BlzFrameSetAbsPoint(b, FRAMEPOINT_CENTER, 0.55, 0.45)
    call BlzFrameSetText(b, "b 3")
    call setup(b)
    call BlzTriggerRegisterFrameEvent(t, b, FRAMEEVENT_CONTROL_CLICK)
    call BlzTriggerRegisterFrameEvent(t2, b, FRAMEEVENT_MOUSE_ENTER)
    //call enable(GetLocalPlayer(),true)
    call BJDebugMsg(GetPlayerName(GetLocalPlayer()))
    call BJDebugMsg("onGameStart END")
endfunction

endlibrary
Спасибо больше ScorpioT1000, за помощь в плавном движении и замечательную вещь экстраполяцию
`
ОЖИДАНИЕ РЕКЛАМЫ...

Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
11
Корректнее будет пересчитать мировые координаты
Блин сама идея крутая. Токо вот работать это будет только с видом сверху , от третьего лица или от первого так не реализуешь. Текущее решение 6-ти летней давности, Самореклама.
Ответы (4)
24
Корректнее будет пересчитать мировые координаты
Блин сама идея крутая. Токо вот работать это будет только с видом сверху , от третьего лица или от первого так не реализуешь.
Реализуешь - считаешь по матрицам трансформации и все, даже при упрощенном варианте Smeto в горизонтальной камере сейчас всего на сантиметр вверх сносит кнопку, это корректируется без проблем, можно даже костылем. Там другая проблема у нас теперь - задержка в событии регистрации движения мыши
12
darkowlom, да, задержка составляет если не ошибаюсь в 0.15 сек
11
darkowlom, интересно, но не совсем понимаю каким образом, если смотреть в небо.
24
darkowlom, интересно, но не совсем понимаю каким образом, если смотреть в небо.
С небом могут быть проблемки, надо будет потестировать, но это можно решить невидимым мостом перед камерой - его игра на уровне ядра считает террейном и, кстати, юнитом тоже
24
Сетка отъедает много памяти, но может будет меньше если ее напрямую в fdf запихать из каких-то очень простых фреймов - надо ставить эксперементы и замерять потребление ресурсов. Я впервые в жизни редактор рефоржа сегодня открыл вообще)
11
его игра считает террейном
Ну тогда это же будут координаты разрушаемого объекта или проекции на землю? Это с любым разрушаемым объектом работает? А что если смотреть по линии горизонта и упираться в стену? Там же координаты высоты нету у мышки. Чет много вопросов. Но тема меня заинтересовала 🤓
Ответы (2)
24
Koladik, Координаты по Z можно получить через локацию, посмотри как это реализовано выше в коде или более наглядно в моей карте RPGEngine, в моих ресурсах есть - там через невидимые мосты сделана система запрыгивания на декорации и приближение камеры когда объект за спиной ее загораживает, это под 1.26a. Там мосты по сути заменяют меш коллизии
Работает с РО на которые можно залезть, обычно это мосты
11
под 1.26a
Да под 1.26 с мем хаком мне кажется проблем отслеживать мышку вообще не должно быть.
12
Вышла новая версия!
Прокрутить к ресурсу
Уменьшил количество Хэндлов до 106 (Победа!)
Убрал FrameGrid, новое решение более оптимизированное через мировые координаты
World2Screen
library World2Screen
globals
    real ScreenX = 0
    real ScreenY = 0
endglobals

function World2Screen takes real x, real y, real z returns nothing
    local real angleOfAttack = -GetCameraField(CAMERA_FIELD_ANGLE_OF_ATTACK)
    local real fieldOfView = GetCameraField(CAMERA_FIELD_FIELD_OF_VIEW)
    local real rotation = GetCameraField(CAMERA_FIELD_ROTATION) + bj_PI

    local real cosAttack = Cos(angleOfAttack)
    local real sinAttack = Sin(angleOfAttack)
    local real cosRot = Cos(rotation)
    local real sinRot = Sin(rotation)

    local real M11 = cosAttack*cosRot
    local real M12 = cosAttack*sinRot
    local real M13 = sinAttack
    local real M21 = -sinRot
    local real M22 = cosRot
    local real M23 = 0
    local real M31 = -cosRot*sinAttack
    local real M32 = -sinAttack*sinRot
    local real M33 = cosAttack

    local real dx = x - GetCameraEyePositionX()
    local real dy = y - GetCameraEyePositionY()
    local real dz = z - GetCameraEyePositionZ()

    local real xPrime = M11*dx + M12*dy + M13*dz
    local real yPrime = M21*dx + M22*dy + M23*dz
    local real zPrime = M31*dx + M32*dy + M33*dz

    local real scaling = 1.04 / ( 2 * Tan(fieldOfView / 2) )

    set ScreenX = 0.4 - (scaling * yPrime) / xPrime
    set ScreenY = 0.355 - (scaling * zPrime) / xPrime
endfunction
endlibrary
Три варианта, какой их них лучше я не понял, ибо каждый работает хорошо, но думаю можно ещё лучше)
1.MoveAbleFrame (Основной)
library MoveAbleFrame initializer onTriggerInit requires World2Screen
globals
real targetX = 0.0
real targetY = 0.0
real currentX = 0.0
real currentY = 0.0
real moveSpeed = 0.04 от 0 до 1: выше — быстрее, ниже — плавнее
real prevTargetX = 0.0
real prevTargetY = 0.0
real deltaX = 0.0
real deltaY = 0.0
endglobals
struct structMoveAbleFrame
boolean Enabled = true

framehandle Frame = null
framehandle PlayerHoveredFrame
framepointtype FramePoint
trigger TriggerFrameEnter
triggeraction TriggerFrameEnterAction
trigger TriggerFrameLeave
triggeraction TriggerFrameLeaveAction
trigger MouseClickTrigger
triggeraction MouseClickTriggerAction
trigger MouseReleaseTrigger
triggeraction MouseReleaseTriggerAction
trigger MOUSE_MOVE_Trigger
triggeraction MOUSE_MOVE_TriggerAction
endstruct
globals
structMoveAbleFrame MoveAbleFrame
endglobals
function enable takes player p, boolean flag returns boolean
if GetLocalPlayer() == p then
if flag == null then
set MoveAbleFrame.Enabled = not MoveAbleFrame.Enabled
else
set MoveAbleFrame.Enabled = flag
endif
endif
return MoveAbleFrame.Enabled
endfunction
function setup takes framehandle frame returns nothing
call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameEnter, frame, FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameLeave , frame, FRAMEEVENT_MOUSE_LEAVE)
endfunction
function startMoving takes framehandle frame, framepointtype framePoint returns nothing
set MoveAbleFrame.Frame = frame
if framePoint == null then
set framePoint = FRAMEPOINT_CENTER
endif
set MoveAbleFrame.FramePoint = framePoint
endfunction
function moveFrame takes real x, real y returns nothing
call BJDebugMsg("moveFrame: called x=" + R2S(x) + " y=" + R2S(y))
call BlzFrameClearAllPoints(MoveAbleFrame.Frame)
call BlzFrameSetAbsPoint(MoveAbleFrame.Frame, MoveAbleFrame.FramePoint, x, y)
endfunction
function GetTriggerPlayerHoveredFrame takes nothing returns nothing
local real x = BlzGetTriggerPlayerMouseX()
local real y = BlzGetTriggerPlayerMouseY()
set GetTriggerName_DEBUG = "TriggerFrameEnter"
if GetLocalPlayer() == GetTriggerPlayer() then
set MoveAbleFrame.PlayerHoveredFrame = BlzGetTriggerFrame()
endif
endfunction
function GetTriggerPlayerHoveredFrameNull takes nothing returns nothing
set GetTriggerName_DEBUG = "TriggerFrameLeave"
if GetLocalPlayer() == GetTriggerPlayer() then
set MoveAbleFrame.PlayerHoveredFrame = null
endif
endfunction
globals
location moveLoc = Location(0, 0)
boolean flag = false
endglobals
function MouseClick takes nothing returns nothing
local player p = GetTriggerPlayer()
local real x = BlzGetTriggerPlayerMouseX()
local real y = BlzGetTriggerPlayerMouseY()
local real z
call updateDebugFrame()
set GetFunctionName_DEBUG = "MouseClick"
set GetTriggerName_DEBUG = "MouseClickTrigger"
call MoveLocation(moveLoc, x, y)
set z = GetLocationZ(moveLoc)
if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
if MoveAbleFrame.PlayerHoveredFrame != null then
call BJDebugMsg("MouseClick: frame is not null: " + BlzFrameGetName(MoveAbleFrame.PlayerHoveredFrame))
if MoveAbleFrame.Enabled then
if GetLocalPlayer() == p then
set flag = true
call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
call BlzFrameSetEnable(MoveAbleFrame.PlayerHoveredFrame, false)
call World2Screen(x,y,z)
set mouseX = ScreenX
set mouseY = ScreenY
set mouseX_prev = ScreenX
set mouseY_prev = ScreenY
set currentX = ScreenX
set currentY = ScreenY
set interpProgress = 1.0
endif
endif
else
call BJDebugMsg("MouseClick: PlayerHoveredFrame is null")
endif
endif
endfunction
function MouseRelease takes nothing returns nothing
set flag = false
set GetFunctionName_DEBUG = "MouseRelease"
set GetTriggerName_DEBUG = "MouseReleaseTrigger"
if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
if MoveAbleFrame.Frame != null then
call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
endif
endif
endfunction
function PolledWait2 takes real time returns nothing
local timer t
local real R
if time>0. then
set t = CreateTimer()
call TimerStart(t,time,false,null)
loop
set R=TimerGetRemaining(t)
exitwhen R<=0.
call TriggerSleepAction(R)
endloop
call DestroyTimer(t)
set t=null
endif
endfunction
globals
real mouseX = 0.0
real mouseY = 0.0
real mouseX_prev = 0.0
real mouseY_prev = 0.0
real mouseX_interp = 0.0
real mouseY_interp = 0.0
real interpProgress = 1.0
real interpSpeed = 0
endglobals
function OnMouseMove takes nothing returns nothing
local real newX = BlzGetTriggerPlayerMouseX()
local real newY = BlzGetTriggerPlayerMouseY()
set GetFunctionName_DEBUG = "OnMouseMove"
set mouseX_prev = mouseX
set mouseY_prev = mouseY
set mouseX = newX
set mouseY = newY
set interpProgress = 0.0
endfunction
function update2 takes nothing returns nothing
local real dist
local real dynamicSpeed
local real dx
local real dy
local real distMouse
if flag == true then
call updateDebugFrame()
set GetFunctionName_DEBUG = "update2"
set dx = mouseX - mouseX_prev
set dy = mouseY - mouseY_prev
set distMouse = SquareRoot(dx * dx + dy * dy)
set interpSpeed = 0.1 + distMouse * 0.6
set interpProgress = interpProgress + interpSpeed
if interpProgress > 1.0 then
set interpProgress = 1.0
endif
set mouseX_interp = mouseX_prev + (mouseX - mouseX_prev) * interpProgress
set mouseY_interp = mouseY_prev + (mouseY - mouseY_prev) * interpProgress
set targetX = mouseX_interp
set targetY = mouseY_interp
set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
set dynamicSpeed = moveSpeed + dist * 0.2
if dynamicSpeed > 0.08 then
set dynamicSpeed = 0.08
endif
set currentX = currentX + (targetX - currentX) * dynamicSpeed
set currentY = currentY + (targetY - currentY) * dynamicSpeed
call moveFrame(currentX, currentY)
endif
endfunction
function CoordsMouse takes nothing returns nothing
local real x = BlzGetTriggerPlayerMouseX()
local real y = BlzGetTriggerPlayerMouseY()
local real z
set GetFunctionName_DEBUG = "CoordsMouse"
set GetTriggerName_DEBUG = "MOUSE_MOVE_Trigger"
if flag == true then
call MoveLocation(moveLoc, x, y)
set z = GetLocationZ(moveLoc)
call World2Screen(x,y,z)
set mouseX_prev = mouseX
set mouseY_prev = mouseY
set mouseX = ScreenX
set mouseY = ScreenY
set interpProgress = 0.0
endif
endfunction
function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = CreateTimer()
local timer Timer2 = CreateTimer()
call PolledWait2(0.01)
set MoveAbleFrame = structMoveAbleFrame.create()

set MoveAbleFrame.TriggerFrameEnter = CreateTrigger()
set MoveAbleFrame.TriggerFrameEnterAction = TriggerAddAction(MoveAbleFrame.TriggerFrameEnter,function GetTriggerPlayerHoveredFrame)

set MoveAbleFrame.TriggerFrameLeave = CreateTrigger()
set MoveAbleFrame.TriggerFrameLeaveAction = TriggerAddAction(MoveAbleFrame.TriggerFrameLeave, function GetTriggerPlayerHoveredFrameNull)
set MoveAbleFrame.MouseClickTrigger = CreateTrigger()
set MoveAbleFrame.MouseClickTriggerAction = TriggerAddAction(MoveAbleFrame.MouseClickTrigger, function MouseClick)

set MoveAbleFrame.MouseReleaseTrigger = CreateTrigger()
set MoveAbleFrame.MouseReleaseTriggerAction = TriggerAddAction(MoveAbleFrame.MouseReleaseTrigger, function MouseRelease)

set MoveAbleFrame.MOUSE_MOVE_Trigger = CreateTrigger()
set MoveAbleFrame.MOUSE_MOVE_TriggerAction = TriggerAddAction(MoveAbleFrame.MOUSE_MOVE_Trigger, function CoordsMouse)
loop
exitwhen playerIndex == GetBJMaxPlayers()
call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseClickTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_DOWN)
call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseReleaseTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_UP)
call TriggerRegisterPlayerEvent(MoveAbleFrame.MOUSE_MOVE_Trigger, Player(playerIndex), EVENT_PLAYER_MOUSE_MOVE)
set playerIndex = playerIndex + 1
endloop
call ExecuteFunc("onGameStart")
call TimerStart(Timer, 0.003, true, function update2)
endfunction
endlibrary
2.MoveAbleFrame 2
library MoveAbleFrame initializer onTriggerInit requires World2Screen

globals
    real targetX = 0.0
    real targetY = 0.0
    real currentX = 0.0
    real currentY = 0.0
    real moveSpeed = 0.04 // от 0 до 1: выше — быстрее, ниже — плавнее
    real prevTargetX = 0.0
    real prevTargetY = 0.0
    real deltaX = 0.0
    real deltaY = 0.0
endglobals

struct structMoveAbleFrame
    boolean         Enabled = true
    
    framehandle     Frame = null
    framehandle     PlayerHoveredFrame
    framepointtype  FramePoint
    trigger         TriggerFrameEnter
    triggeraction   TriggerFrameEnterAction
    trigger         TriggerFrameLeave
    triggeraction   TriggerFrameLeaveAction
    trigger         MouseClickTrigger
    triggeraction   MouseClickTriggerAction
    trigger         MouseReleaseTrigger
    triggeraction   MouseReleaseTriggerAction
    trigger         MOUSE_MOVE_Trigger
    triggeraction   MOUSE_MOVE_TriggerAction
endstruct

globals
    structMoveAbleFrame MoveAbleFrame
endglobals

function enable takes player p, boolean flag returns boolean
    if GetLocalPlayer() == p then
        if flag == null then
            set MoveAbleFrame.Enabled = not MoveAbleFrame.Enabled
        else
            set MoveAbleFrame.Enabled = flag
        endif
    endif
    return MoveAbleFrame.Enabled
endfunction

function setup takes framehandle frame returns nothing
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameEnter, frame, FRAMEEVENT_MOUSE_ENTER)
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameLeave , frame, FRAMEEVENT_MOUSE_LEAVE)
endfunction


function startMoving takes framehandle frame, framepointtype framePoint returns nothing
    set MoveAbleFrame.Frame = frame
    if framePoint == null then
       set framePoint = FRAMEPOINT_CENTER
    endif
    set MoveAbleFrame.FramePoint = framePoint
endfunction


function moveFrame takes real x, real y returns nothing
    call BJDebugMsg("moveFrame: called x=" + R2S(x) + " y=" + R2S(y))
    call BlzFrameClearAllPoints(MoveAbleFrame.Frame)
    call BlzFrameSetAbsPoint(MoveAbleFrame.Frame, MoveAbleFrame.FramePoint, x, y)
endfunction



function GetTriggerPlayerHoveredFrame takes nothing returns nothing
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = BlzGetTriggerFrame()
    endif
endfunction

function GetTriggerPlayerHoveredFrameNull takes nothing returns nothing
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = null
    endif
endfunction

globals
    location moveLoc = Location(0, 0)
    boolean flag = false
endglobals

function MouseClick takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z

    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)

    call BJDebugMsg("MouseClick")

    if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
        if MoveAbleFrame.PlayerHoveredFrame != null then
            call BJDebugMsg("MouseClick: frame is not null: " + BlzFrameGetName(MoveAbleFrame.PlayerHoveredFrame))
            if MoveAbleFrame.Enabled then
                if GetLocalPlayer() == p then
                    set flag = true // Перемещение только если действительно кликнули по фрейму
                    call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
                    call BlzFrameSetEnable(MoveAbleFrame.PlayerHoveredFrame, false)
                    call World2Screen(x,y,z)
                endif
            endif
        else
            call BJDebugMsg("MouseClick: PlayerHoveredFrame is null")
        endif
    endif
endfunction

function MouseRelease takes nothing returns nothing
call BJDebugMsg("MouseRelease")
set flag = false
    if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
        if MoveAbleFrame.Frame != null then
            call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
        endif
    endif
endfunction

function PolledWait2 takes real time returns nothing
local timer t
local real R
    if time>0. then
        set t = CreateTimer()
        call TimerStart(t,time,false,null)
        loop
            set R=TimerGetRemaining(t)
            exitwhen R<=0.
                call TriggerSleepAction(R)
        endloop
        call DestroyTimer(t)
        set t=null
    endif 
endfunction



function update2 takes nothing returns nothing
    local real dist
    local real dynamicSpeed
    if flag == true then
        set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
        set dynamicSpeed = moveSpeed + dist * 0.4
        if dynamicSpeed > 0.08 then
            set dynamicSpeed = 0.08
        endif
        // Плавное приближение к целевой позиции
        set currentX = currentX + (targetX - currentX) * moveSpeed
        set currentY = currentY + (targetY - currentY) * moveSpeed

        call moveFrame(currentX, currentY)
    endif
endfunction

function CoordsMouse takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real speedLimit = 0.25
    local real deltaLength = SquareRoot(deltaX * deltaX + deltaY * deltaY)
    local real z
    if flag == true then
        call MoveLocation(moveLoc, x, y)
        set z = GetLocationZ(moveLoc)
        call World2Screen(x,y,z)
        set deltaX = ScreenX - prevTargetX
        set deltaY = ScreenY - prevTargetY
        if deltaLength > speedLimit then
            set deltaX = deltaX * speedLimit / deltaLength
            set deltaY = deltaY * speedLimit / deltaLength
        endif
        set targetX = ScreenX + deltaX * 1.4
        set targetY = ScreenY + deltaY * 1.4

        set prevTargetX = ScreenX
        set prevTargetY = ScreenY
    endif
endfunction

function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = CreateTimer()
local timer Timer2 = CreateTimer()
    call PolledWait2(0.01)
    set MoveAbleFrame = structMoveAbleFrame.create()
    
    set MoveAbleFrame.TriggerFrameEnter = CreateTrigger()
    set MoveAbleFrame.TriggerFrameEnterAction = TriggerAddAction(MoveAbleFrame.TriggerFrameEnter,function GetTriggerPlayerHoveredFrame)
    
    set MoveAbleFrame.TriggerFrameLeave = CreateTrigger()
    set MoveAbleFrame.TriggerFrameLeaveAction = TriggerAddAction(MoveAbleFrame.TriggerFrameLeave, function GetTriggerPlayerHoveredFrameNull)

    set MoveAbleFrame.MouseClickTrigger = CreateTrigger()
    set MoveAbleFrame.MouseClickTriggerAction = TriggerAddAction(MoveAbleFrame.MouseClickTrigger, function MouseClick)
    
    set MoveAbleFrame.MouseReleaseTrigger = CreateTrigger()
    set MoveAbleFrame.MouseReleaseTriggerAction = TriggerAddAction(MoveAbleFrame.MouseReleaseTrigger, function MouseRelease)
    
    set MoveAbleFrame.MOUSE_MOVE_Trigger = CreateTrigger()
    set MoveAbleFrame.MOUSE_MOVE_TriggerAction = TriggerAddAction(MoveAbleFrame.MOUSE_MOVE_Trigger, function CoordsMouse)

    loop
    exitwhen playerIndex == GetBJMaxPlayers()
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseClickTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_DOWN)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseReleaseTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_UP)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MOUSE_MOVE_Trigger, Player(playerIndex), EVENT_PLAYER_MOUSE_MOVE)
        set playerIndex = playerIndex + 1
    endloop
call ExecuteFunc("onGameStart")
call TimerStart(Timer, 0.003, true, function update2)
endfunction

endlibrary

3. MoveAbleFrame 3 (Можно подогнать скорость под таймер 0.001 и будет ещё лучше)
library MoveAbleFrame initializer onTriggerInit requires World2Screen
globals
real targetX = 0.0
real targetY = 0.0
real currentX = 0.0
real currentY = 0.0
real moveSpeed = 0.02 от 0 до 1: выше — быстрее, ниже — плавнее
real prevTargetX = 0.0
real prevTargetY = 0.0
real deltaX = 0.0
real deltaY = 0.0
endglobals
struct structMoveAbleFrame
boolean Enabled = true

framehandle Frame = null
framehandle PlayerHoveredFrame
framepointtype FramePoint
trigger TriggerFrameEnter
triggeraction TriggerFrameEnterAction
trigger TriggerFrameLeave
triggeraction TriggerFrameLeaveAction
trigger MouseClickTrigger
triggeraction MouseClickTriggerAction
trigger MouseReleaseTrigger
triggeraction MouseReleaseTriggerAction
trigger MOUSE_MOVE_Trigger
triggeraction MOUSE_MOVE_TriggerAction
endstruct
globals
structMoveAbleFrame MoveAbleFrame
endglobals
function enable takes player p, boolean flag returns boolean
if GetLocalPlayer() == p then
if flag == null then
set MoveAbleFrame.Enabled = not MoveAbleFrame.Enabled
else
set MoveAbleFrame.Enabled = flag
endif
endif
return MoveAbleFrame.Enabled
endfunction
function setup takes framehandle frame returns nothing
call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameEnter, frame, FRAMEEVENT_MOUSE_ENTER)
call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameLeave , frame, FRAMEEVENT_MOUSE_LEAVE)
endfunction
function startMoving takes framehandle frame, framepointtype framePoint returns nothing
set MoveAbleFrame.Frame = frame
if framePoint == null then
set framePoint = FRAMEPOINT_CENTER
endif
set MoveAbleFrame.FramePoint = framePoint
endfunction
function moveFrame takes real x, real y returns nothing
call BJDebugMsg("moveFrame: called x=" + R2S(x) + " y=" + R2S(y))
call BlzFrameClearAllPoints(MoveAbleFrame.Frame)
call BlzFrameSetAbsPoint(MoveAbleFrame.Frame, MoveAbleFrame.FramePoint, x, y)
endfunction
function GetTriggerPlayerHoveredFrame takes nothing returns nothing
local real x = BlzGetTriggerPlayerMouseX()
local real y = BlzGetTriggerPlayerMouseY()
set GetTriggerName_DEBUG = "TriggerFrameEnter"
if GetLocalPlayer() == GetTriggerPlayer() then
set MoveAbleFrame.PlayerHoveredFrame = BlzGetTriggerFrame()
endif
endfunction
function GetTriggerPlayerHoveredFrameNull takes nothing returns nothing
set GetTriggerName_DEBUG = "TriggerFrameLeave"
if GetLocalPlayer() == GetTriggerPlayer() then
set MoveAbleFrame.PlayerHoveredFrame = null
endif
endfunction
globals
location moveLoc = Location(0, 0)
boolean flag = false
endglobals
function MouseClick takes nothing returns nothing
local player p = GetTriggerPlayer()
local real x = BlzGetTriggerPlayerMouseX()
local real y = BlzGetTriggerPlayerMouseY()
local real z
call updateDebugFrame()
set GetFunctionName_DEBUG = "MouseClick"
set GetTriggerName_DEBUG = "MouseClickTrigger"
call MoveLocation(moveLoc, x, y)
set z = GetLocationZ(moveLoc)
if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
if MoveAbleFrame.PlayerHoveredFrame != null then
call BJDebugMsg("MouseClick: frame is not null: " + BlzFrameGetName(MoveAbleFrame.PlayerHoveredFrame))
if MoveAbleFrame.Enabled then
if GetLocalPlayer() == p then
set flag = true
call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
call BlzFrameSetEnable(MoveAbleFrame.PlayerHoveredFrame, false)
call World2Screen(x,y,z)
set mouseX = ScreenX
set mouseY = ScreenY
set mouseX_prev = ScreenX
set mouseY_prev = ScreenY
set currentX = ScreenX
set currentY = ScreenY
set interpProgress = 1.0
endif
endif
else
call BJDebugMsg("MouseClick: PlayerHoveredFrame is null")
endif
endif
endfunction
function MouseRelease takes nothing returns nothing
set flag = false
set GetFunctionName_DEBUG = "MouseRelease"
set GetTriggerName_DEBUG = "MouseReleaseTrigger"
if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
if MoveAbleFrame.Frame != null then
call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
endif
endif
endfunction
function PolledWait2 takes real time returns nothing
local timer t
local real R
if time>0. then
set t = CreateTimer()
call TimerStart(t,time,false,null)
loop
set R=TimerGetRemaining(t)
exitwhen R<=0.
call TriggerSleepAction(R)
endloop
call DestroyTimer(t)
set t=null
endif
endfunction
globals
real mouseX = 0.0
real mouseY = 0.0
real mouseX_prev = 0.0
real mouseY_prev = 0.0
real mouseX_interp = 0.0
real mouseY_interp = 0.0
real interpProgress = 1.0
real interpSpeed = 0
endglobals
function OnMouseMove takes nothing returns nothing
local real newX = BlzGetTriggerPlayerMouseX()
local real newY = BlzGetTriggerPlayerMouseY()
set GetFunctionName_DEBUG = "OnMouseMove"
set mouseX_prev = mouseX
set mouseY_prev = mouseY
set mouseX = newX
set mouseY = newY
set interpProgress = 0.0
endfunction
function update2 takes nothing returns nothing
local real dist
local real dynamicSpeed
local real dx
local real dy
local real distMouse
if flag == true then
call updateDebugFrame()
set GetTriggerName_DEBUG = "onTriggerInit"
set GetFunctionName_DEBUG = "update2"
set dx = mouseX - mouseX_prev
set dy = mouseY - mouseY_prev
set distMouse = SquareRoot(dx * dx + dy * dy)
set interpSpeed = 0.04 + distMouse * 0.3
set interpProgress = interpProgress + interpSpeed
if interpProgress > 1.0 then
set interpProgress = 1.0
endif
set mouseX_interp = mouseX_prev + (mouseX - mouseX_prev) * interpProgress
set mouseY_interp = mouseY_prev + (mouseY - mouseY_prev) * interpProgress
set targetX = mouseX_interp
set targetY = mouseY_interp
set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
set dynamicSpeed = moveSpeed + dist * 0.1
if dynamicSpeed > 0.05 then
set dynamicSpeed = 0.05
endif
set currentX = currentX + (targetX - currentX) * dynamicSpeed
set currentY = currentY + (targetY - currentY) * dynamicSpeed
call moveFrame(currentX, currentY)
endif
endfunction
function CoordsMouse takes nothing returns nothing
local real x = BlzGetTriggerPlayerMouseX()
local real y = BlzGetTriggerPlayerMouseY()
local real z
set GetFunctionName_DEBUG = "CoordsMouse"
set GetTriggerName_DEBUG = "MOUSE_MOVE_Trigger"
if flag == true then
call MoveLocation(moveLoc, x, y)
set z = GetLocationZ(moveLoc)
call World2Screen(x,y,z)
set mouseX_prev = mouseX
set mouseY_prev = mouseY
set mouseX = ScreenX
set mouseY = ScreenY
set interpProgress = 0.0
endif
endfunction
function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = CreateTimer()
local timer Timer2 = CreateTimer()
call PolledWait2(0.01)
set MoveAbleFrame = structMoveAbleFrame.create()

set MoveAbleFrame.TriggerFrameEnter = CreateTrigger()
set MoveAbleFrame.TriggerFrameEnterAction = TriggerAddAction(MoveAbleFrame.TriggerFrameEnter,function GetTriggerPlayerHoveredFrame)

set MoveAbleFrame.TriggerFrameLeave = CreateTrigger()
set MoveAbleFrame.TriggerFrameLeaveAction = TriggerAddAction(MoveAbleFrame.TriggerFrameLeave, function GetTriggerPlayerHoveredFrameNull)
set MoveAbleFrame.MouseClickTrigger = CreateTrigger()
set MoveAbleFrame.MouseClickTriggerAction = TriggerAddAction(MoveAbleFrame.MouseClickTrigger, function MouseClick)

set MoveAbleFrame.MouseReleaseTrigger = CreateTrigger()
set MoveAbleFrame.MouseReleaseTriggerAction = TriggerAddAction(MoveAbleFrame.MouseReleaseTrigger, function MouseRelease)

set MoveAbleFrame.MOUSE_MOVE_Trigger = CreateTrigger()
set MoveAbleFrame.MOUSE_MOVE_TriggerAction = TriggerAddAction(MoveAbleFrame.MOUSE_MOVE_Trigger, function CoordsMouse)
loop
exitwhen playerIndex == GetBJMaxPlayers()
call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseClickTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_DOWN)
call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseReleaseTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_UP)
call TriggerRegisterPlayerEvent(MoveAbleFrame.MOUSE_MOVE_Trigger, Player(playerIndex), EVENT_PLAYER_MOUSE_MOVE)
set playerIndex = playerIndex + 1
endloop
call ExecuteFunc("onGameStart")
call TimerStart(Timer, 0.001, true, function update2)
endfunction
endlibrary
Ответы (3)
24
Smeto, по-возможности сделай версию, которая будет открываться сразу на 2.0, для совместимости и удобства пользователей
P.S продолжаю эксперименты в попытках обойти задержку события, может что-то накопаю
12
darkowlom, только что обнаружил, что можно по таймеру запускать call BlzFrameClick(MoveAbleFrame.PlayerHoveredFrame), то есть будет срабатывать событие, по которому можно получать координаты и это уже ускоряет процесс.
Надо будет понять есть ли ещё варианты, ну и применить как-то для более идеальной плавности :)
24
Smeto, значит надо в течение движения включать флаг, который отдельным таймером будет вызывать клик, а в реакции на клик по флагу движения отключать основное действие и перезаписывать текущие координаты. Таймер с частотой 0.01 выполнять 15 раз обновляя в нем позицию фрейма вызовом клика, пока движение не регистрируется. Флаг движения надо обнулять не по событию прекращения движения, а по отпускания правой кнопки мыши, что бы обработчик клика корректно отрабатывал в интервал пока идёт задержка события.
Можно так попробовать как-то
12
Вышла новая версия!
Прокрутить к ресурсу
Добавил версию карты которая открывается на Warcraft 3 Reforged 2.0.0
Скачать версию карты для Warcraft 3 Reforget 2.0.0
Загруженные файлы
12
Вышла новая версия!
Прокрутить к ресурсу
Обновление системы перемещения фреймов
🔹 Новые возможности:
✅ Два режима перемещения:
🔘 Удержание ПКМ – фрейм движется, пока зажата правая кнопка мыши.
🔘 Переключение по клику – первый клик ПКМ активирует движение, второй – сбрасывает.
✅ Плавное завершение анимации – фрейм теперь всегда доходит до конечной точки, а не останавливается раньше.
✅ Автоматический сброс масштаба – после завершения движения фрейм возвращается к стандартному размеру (scale = 1.0).
🔹 Исправленные баги:
🛠 Фрейм не возвращался к исходному размеру после клика по нему (теперь работает корректно).
🛠 Прерывание движения – раньше фрейм мог не доходить до цели, теперь анимация всегда завершается.
💡 Оптимизации:
Улучшена плавность движения.
Устранены рывки при резком перемещении мыши.
Логика сброса стала надёжнее (теперь фрейм не застревает в уменьшенном состоянии).
Не смог разобрать, но что то утекает когда я использую таймеры:
function PolledWait2 takes real time returns nothing
local timer t
local real R
    if time>0. then
        set t = CreateTimer()
        call TimerStart(t,time,false,null)
        loop
            set R=TimerGetRemaining(t)
            exitwhen R<=0.
                call TriggerSleepAction(R)
        endloop
        call DestroyTimer(t)
        set t=null
    endif 
endfunction
Обновлённый код движения:
library MoveAbleFrame initializer onTriggerInit requires World2Screen

globals
    real targetX = 0.0
    real targetY = 0.0
    real currentX = 0.0
    real currentY = 0.0
    real moveSpeed = 0.01 // от 0 до 1: выше — быстрее, ниже — плавнее
    real prevTargetX = 0.0
    real prevTargetY = 0.0
    real deltaX = 0.0
    real deltaY = 0.0
    boolean wasMoving // Флаг для того, чтоб сделать фрейм размером 1 после догонки
    boolean PolledMoving = false// Флаг для того, чтоб сделать фрейм размером 1 после догонки
endglobals

struct structMoveAbleFrame
    boolean         Enabled = true
    
    framehandle     Frame = null
    framehandle     PlayerHoveredFrame
    framepointtype  FramePoint
    trigger         TriggerFrameEnter
    triggeraction   TriggerFrameEnterAction
    trigger         TriggerFrameLeave
    triggeraction   TriggerFrameLeaveAction
    trigger         MouseClickTrigger
    triggeraction   MouseClickTriggerAction
    trigger         MouseReleaseTrigger
    triggeraction   MouseReleaseTriggerAction
    trigger         MOUSE_MOVE_Trigger
    triggeraction   MOUSE_MOVE_TriggerAction
endstruct

globals
    structMoveAbleFrame MoveAbleFrame
endglobals

function enable takes player p, boolean flag returns boolean
    if GetLocalPlayer() == p then
        if flag == null then
            set MoveAbleFrame.Enabled = not MoveAbleFrame.Enabled
        else
            set MoveAbleFrame.Enabled = flag
        endif
    endif
    return MoveAbleFrame.Enabled
endfunction

function setup takes framehandle frame returns nothing
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameEnter, frame, FRAMEEVENT_MOUSE_ENTER)
    call BlzTriggerRegisterFrameEvent(MoveAbleFrame.TriggerFrameLeave , frame, FRAMEEVENT_MOUSE_LEAVE)
endfunction


function startMoving takes framehandle frame, framepointtype framePoint returns nothing
    set MoveAbleFrame.Frame = frame
    if framePoint == null then
       set framePoint = FRAMEPOINT_CENTER
    endif
    set MoveAbleFrame.FramePoint = framePoint
endfunction


function moveFrame takes real x, real y returns nothing
    call BlzFrameClearAllPoints(MoveAbleFrame.Frame)
    call BlzFrameSetAbsPoint(MoveAbleFrame.Frame, MoveAbleFrame.FramePoint, x, y)
endfunction


function GetTriggerPlayerHoveredFrame takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = BlzGetTriggerFrame()
    endif
endfunction

function GetTriggerPlayerHoveredFrameNull takes nothing returns nothing
    if GetLocalPlayer() == GetTriggerPlayer() then
        set MoveAbleFrame.PlayerHoveredFrame = null
    endif
endfunction

globals
    location moveLoc = Location(0, 0)
    boolean flag = false
endglobals

function TempFrameSetting takes nothing returns nothing
    call BlzFrameSetEnable(MoveAbleFrame.PlayerHoveredFrame, false)
    call BlzFrameSetLevel(MoveAbleFrame.PlayerHoveredFrame, 9)
    call BlzFrameSetScale(MoveAbleFrame.PlayerHoveredFrame, 0.8)
endfunction

function NormalFrameSetting takes nothing returns nothing
    call BlzFrameSetScale(MoveAbleFrame.Frame, 1.0)
    call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
    call BlzFrameSetLevel(MoveAbleFrame.Frame, 6)
    set MoveAbleFrame.Frame = null
endfunction


globals
    real mouseX = 0.0
    real mouseY = 0.0
    real mouseX_prev = 0.0
    real mouseY_prev = 0.0
    real mouseX_interp = 0.0
    real mouseY_interp = 0.0
    real interpProgress = 1.0
    real interpSpeed  = 0
endglobals

function UpdateTargetXYInstant takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z

    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)
    call World2Screen(x, y, z)

    // не трогаем prev/interp — сразу задаём target
    set targetX = ScreenX
    set targetY = ScreenY
endfunction
function UpdateMouseScreenCoords takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z

    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)
    call World2Screen(x, y, z)

    set mouseX_prev = mouseX
    set mouseY_prev = mouseY
    set mouseX = ScreenX
    set mouseY = ScreenY
    set mouseX_interp = mouseX
    set mouseY_interp = mouseY
    set interpProgress = 1.0

    set targetX = mouseX
    set targetY = mouseY
endfunction

function PolledWait2 takes real time returns nothing
local timer t
local real R
    if time>0. then
        set t = CreateTimer()
        call TimerStart(t,time,false,null)
        loop
            set R=TimerGetRemaining(t)
            exitwhen R<=0.
                call TriggerSleepAction(R)
        endloop
        call DestroyTimer(t)
        set t=null
    endif 
endfunction

function GetWorld2Screen takes nothing returns real
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z
    
    call MoveLocation(moveLoc, x, y)
    set z = GetLocationZ(moveLoc)
    call World2Screen(x, y, z)
    return SquareRoot((currentX - ScreenX)*(currentX - ScreenX) + (currentY - ScreenY)*(currentY - ScreenY))
endfunction

function MouseClick takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local real dist
    local timer t
    if BlzGetTriggerPlayerMouseButton() == MOUSE_BUTTON_TYPE_RIGHT then
        if MoveAbleFrame.Enabled and GetLocalPlayer() == p then
            if RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE then
                // Стандартное поведение (перетаскивание)
                if MoveAbleFrame.PlayerHoveredFrame != null then
                    set flag = true
                    call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
                    call TempFrameSetting()
                    call UpdateMouseScreenCoords() 
                    set currentX = targetX
                    set currentY = targetY
                    call moveFrame(currentX, currentY)
                else
                    set flag = false
                    set MoveAbleFrame.Frame = null
                endif
            else
                // Альтернативное поведение (вкл/выкл)
                if MoveAbleFrame.PlayerHoveredFrame != null then
                    set flag = not flag
                    set PolledMoving = not PolledMoving
                    if flag then
                        call startMoving(MoveAbleFrame.PlayerHoveredFrame, FRAMEPOINT_CENTER)
                        call UpdateMouseScreenCoords()
                        set currentX = targetX
                        set currentY = targetY
                        call moveFrame(currentX, currentY)
                        call TempFrameSetting()
                    else
                        // Добавлено: сброс масштаба при flag = false (клик по фрейму)
                        call NormalFrameSetting()
                    endif
                else
                    // Клик вне фрейма — сброс (если PolledMoving = true)
                    if DRAG_TIMER_ENABLE then
                        if PolledMoving then
                            set PolledMoving = not PolledMoving
                            if GetWorld2Screen() > 0.0029 then
                                set t = CreateTimer()
                                call TimerStart(t, 0.15, false, null)
                                loop
                                    set dist = GetWorld2Screen()
                                    exitwhen dist < 0.002 or TimerGetRemaining(t) <= 0.
                                    call TriggerSleepAction(0.01)
                                endloop
                                call DestroyTimer(t)
                                set t = null
                            endif
                        endif
                    endif
                    set flag = false
                    call NormalFrameSetting()
                endif 
            endif
        endif
    endif
endfunction

function MouseRelease takes nothing returns nothing
    local real dist
    if MoveAbleFrame.Frame == null then
        return // нет фрейма — ничего не двигаем
    endif
    if RIGHT_MOUSE_BUTTON_FRAME_DRAG_ENABLE then
        set dist = GetWorld2Screen()

        if dist < 0.003 then
            // Мышь уже почти на фрейме — завершаем немедленно
            set flag = false
            set wasMoving = true
            set targetX = ScreenX
            set targetY = ScreenY
        else
            if DRAG_TIMER_ENABLE then
                 // Мышь далеко — ждём, пока дотянется
                call PolledWait2(0.15)
            endif
            set flag = false
            set wasMoving = true
            call UpdateTargetXYInstant()
        endif 
    endif
endfunction

function OnMouseMove takes nothing returns nothing
    local real newX = BlzGetTriggerPlayerMouseX()
    local real newY = BlzGetTriggerPlayerMouseY()
    //set GetFunctionName_DEBUG = "OnMouseMove"
    set mouseX_prev = mouseX
    set mouseY_prev = mouseY

    set mouseX = newX
    set mouseY = newY
    set interpProgress = 0.0
endfunction

function update2 takes nothing returns nothing
    local real dx
    local real dy
    local real distMouse
    local real dist
    local real dynamicSpeed
    local real speed = moveSpeed
    if MoveAbleFrame.Frame == null then
        return // нет фрейма — ничего не двигаем
    endif
    if flag or wasMoving then
        set dx = mouseX - mouseX_prev
        set dy = mouseY - mouseY_prev
        set distMouse = SquareRoot(dx * dx + dy * dy)

        set interpSpeed = 0.1 + distMouse * 0.6
        if interpSpeed > 0.3 then
            set interpSpeed = 0.3 // Ограничение скорости интерполяции
        endif
        set interpProgress = interpProgress + interpSpeed
        if interpProgress > 1.0 then
            set interpProgress = 1.0
        endif

        set mouseX_interp = mouseX_prev + (mouseX - mouseX_prev) * interpProgress
        set mouseY_interp = mouseY_prev + (mouseY - mouseY_prev) * interpProgress

        set targetX = mouseX_interp
        set targetY = mouseY_interp

        //set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
        //set dynamicSpeed = moveSpeed + dist * 0.4
        //if dynamicSpeed > 0.08 then
            //set dynamicSpeed = 0.08
        //endif

        //set currentX = currentX + (targetX - currentX) * dynamicSpeed
        //set currentY = currentY + (targetY - currentY) * dynamicSpeed
        set dist = SquareRoot((targetX - currentX)*(targetX - currentX) + (targetY - currentY)*(targetY - currentY))
        if dist < 0.05 then
            set speed = moveSpeed + (0.05 - dist) * 2.0
            if speed > 0.025 then
                set speed = 0.025 // максимум, чтобы не прыгало
            endif
        endif

        set currentX = currentX * (1.0 - speed) + targetX * speed
        set currentY = currentY * (1.0 - speed) + targetY * speed

        call moveFrame(currentX, currentY)
        

        // завершение
        if not flag and dist < 0.001 then
            set wasMoving = false
            call BlzFrameSetScale(MoveAbleFrame.Frame, 1.0)
            call BlzFrameSetEnable(MoveAbleFrame.Frame, true)
            call BlzFrameSetLevel(MoveAbleFrame.Frame, 6)
        endif
    endif
endfunction


function CoordsMouse takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z
    if flag == true then
        call MoveLocation(moveLoc, x, y)
        set z = GetLocationZ(moveLoc)
        call World2Screen(x,y,z)

        set mouseX_prev = mouseX
        set mouseY_prev = mouseY
        set mouseX = ScreenX
        set mouseY = ScreenY
        set interpProgress = 0.0
    endif
endfunction

function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = CreateTimer()
local timer Timer2 = CreateTimer()
    call PolledWait2(0.01)
    set MoveAbleFrame = structMoveAbleFrame.create()
    
    set MoveAbleFrame.TriggerFrameEnter = CreateTrigger()
    set MoveAbleFrame.TriggerFrameEnterAction = TriggerAddAction(MoveAbleFrame.TriggerFrameEnter,function GetTriggerPlayerHoveredFrame)
    
    set MoveAbleFrame.TriggerFrameLeave = CreateTrigger()
    set MoveAbleFrame.TriggerFrameLeaveAction = TriggerAddAction(MoveAbleFrame.TriggerFrameLeave, function GetTriggerPlayerHoveredFrameNull)

    set MoveAbleFrame.MouseClickTrigger = CreateTrigger()
    set MoveAbleFrame.MouseClickTriggerAction = TriggerAddAction(MoveAbleFrame.MouseClickTrigger, function MouseClick)
    
    set MoveAbleFrame.MouseReleaseTrigger = CreateTrigger()
    set MoveAbleFrame.MouseReleaseTriggerAction = TriggerAddAction(MoveAbleFrame.MouseReleaseTrigger, function MouseRelease)
    
    set MoveAbleFrame.MOUSE_MOVE_Trigger = CreateTrigger()
    set MoveAbleFrame.MOUSE_MOVE_TriggerAction = TriggerAddAction(MoveAbleFrame.MOUSE_MOVE_Trigger, function CoordsMouse)

    loop
    exitwhen playerIndex == GetBJMaxPlayers()
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseClickTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_DOWN)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MouseReleaseTrigger, Player(playerIndex), EVENT_PLAYER_MOUSE_UP)
        call TriggerRegisterPlayerEvent(MoveAbleFrame.MOUSE_MOVE_Trigger, Player(playerIndex), EVENT_PLAYER_MOUSE_MOVE)
        set playerIndex = playerIndex + 1
    endloop
call ExecuteFunc("Init")
call TimerStart(Timer, 0.001, true, function update2)
call ExecuteFunc("CheckBoxInit")
endfunction

endlibrary

Ответы (3)
26
function PolledWait2 takes real time returns nothing
local timer t
local real R
    if time>0. then
        set t = CreateTimer()
        call TimerStart(t,time,false,null)
        loop
            set R=TimerGetRemaining(t)
            exitwhen R<=0.
                call TriggerSleepAction(R)
        endloop
        call DestroyTimer(t)
        set t=null
    endif 
endfunction
Что это вообще такое и для чего?
12
Makeba, Обычный плавный таймер, обычно не утекает, но в данном кейсе (Видимо много действий) утекает
Он срабатывает для того, что после прекращения движения(Клик ПКМ), если дистанция далека от конечной точки(Клика ПКМ), через 0.015 сек (Уж точно получить координаты корректные, ведь событие срабатывает не чаще примерно чем раз в 0.15сек) прибыть в нужную точку. Компенсация грубо говоря координат, но конкретно этот участок вызывает десинхрон в Мультиплеере, поэтому я ввёл константу DRAG_TIMER_ENABLE, чтоб кому нужно отключили таймеры
26
Smeto, обычный таймер — это обычный таймер, а это какая-то кастомная функция с TriggerSleepAction. Не знаю, что там утекает или не утекает, но почему нельзя голый нативный таймер использовать?
Показан только небольшой набор комментариев вокруг указанного. Перейти к актуальным.
Чтобы оставить комментарий, пожалуйста, войдите на сайт.