Способ реализации:
Версия 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 сек
Думаю эту систему можно сделать ещё более плавной, если будут идеи дайте знать в комментариях

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

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

>> 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

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

<<
Код для тестирования системы
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
`
ОЖИДАНИЕ РЕКЛАМЫ...
24
Корректнее будет пересчитать мировые координаты в экранные на основе параметров камеры и пропорций экрана, по-моему, реализация для старых патчей была в DGUI - стоит попробовать портировать функцию оттуда

ИИ-шный код на JASS. Не тестировал, но на первый взгляд выглядит корректно
Код
// Глобальные переменные для результата конвертации
globals
    integer udg_ScreenX = 0
    integer udg_ScreenY = 0
    real udg_ScreenDepth = 0.0
endglobals

// Умножение матриц 4x4 (результат в matrixC)
function MultiplyMatrix4x4 takes real array matrixA, real array matrixB, real array matrixC returns nothing
    local integer i = 0
    local integer j = 0
    local integer k = 0
    
    loop
        exitwhen i > 3
        set j = 0
        loop
            exitwhen j > 3
            set matrixC[i * 4 + j] = 0.0
            set k = 0
            loop
                exitwhen k > 3
                set matrixC[i * 4 + j] = matrixC[i * 4 + j] + matrixA[i * 4 + k] * matrixB[k * 4 + j]
                set k = k + 1
            endloop
            set j = j + 1
        endloop
        set i = i + 1
    endloop
endfunction

// Создание матрицы вида на основе параметров камеры
function CreateViewMatrix takes real camX, real camY, real camZ, real yawDeg, real pitchDeg, real rollDeg, real array viewMatrix returns nothing
    local real yawRad = yawDeg * 3.141592653589793 / 180.0
    local real pitchRad = pitchDeg * 3.141592653589793 / 180.0
    local real rollRad = rollDeg * 3.141592653589793 / 180.0
    
    local real cosY = Cos(yawRad)
    local real sinY = Sin(yawRad)
    local real cosX = Cos(pitchRad)
    local real sinX = Sin(pitchRad)
    local real cosZ = Cos(rollRad)
    local real sinZ = Sin(rollRad)
    
    // Временные матрицы
    local real array rotY[16]
    local real array rotX[16]
    local real array rotZ[16]
    local real array rotation[16]
    local real array translation[16]
    local real array temp[16]
    
    // Матрица поворота Y (Yaw)
    set rotY[0] = cosY    // [0,0]
    set rotY[1] = 0.0     // [0,1]
    set rotY[2] = sinY    // [0,2]
    set rotY[3] = 0.0     // [0,3]
    set rotY[4] = 0.0     // [1,0]
    set rotY[5] = 1.0     // [1,1]
    set rotY[6] = 0.0     // [1,2]
    set rotY[7] = 0.0     // [1,3]
    set rotY[8] = -sinY   // [2,0]
    set rotY[9] = 0.0     // [2,1]
    set rotY[10] = cosY   // [2,2]
    set rotY[11] = 0.0    // [2,3]
    set rotY[12] = 0.0    // [3,0]
    set rotY[13] = 0.0    // [3,1]
    set rotY[14] = 0.0    // [3,2]
    set rotY[15] = 1.0    // [3,3]
    
    // Матрица поворота X (Pitch)
    set rotX[0] = 1.0     // [0,0]
    set rotX[1] = 0.0     // [0,1]
    set rotX[2] = 0.0     // [0,2]
    set rotX[3] = 0.0     // [0,3]
    set rotX[4] = 0.0     // [1,0]
    set rotX[5] = cosX    // [1,1]
    set rotX[6] = -sinX   // [1,2]
    set rotX[7] = 0.0     // [1,3]
    set rotX[8] = 0.0     // [2,0]
    set rotX[9] = sinX    // [2,1]
    set rotX[10] = cosX   // [2,2]
    set rotX[11] = 0.0    // [2,3]
    set rotX[12] = 0.0    // [3,0]
    set rotX[13] = 0.0    // [3,1]
    set rotX[14] = 0.0    // [3,2]
    set rotX[15] = 1.0    // [3,3]
    
    // Матрица поворота Z (Roll)
    set rotZ[0] = cosZ    // [0,0]
    set rotZ[1] = -sinZ   // [0,1]
    set rotZ[2] = 0.0     // [0,2]
    set rotZ[3] = 0.0     // [0,3]
    set rotZ[4] = sinZ    // [1,0]
    set rotZ[5] = cosZ    // [1,1]
    set rotZ[6] = 0.0     // [1,2]
    set rotZ[7] = 0.0     // [1,3]
    set rotZ[8] = 0.0     // [2,0]
    set rotZ[9] = 0.0     // [2,1]
    set rotZ[10] = 1.0    // [2,2]
    set rotZ[11] = 0.0    // [2,3]
    set rotZ[12] = 0.0    // [3,0]
    set rotZ[13] = 0.0    // [3,1]
    set rotZ[14] = 0.0    // [3,2]
    set rotZ[15] = 1.0    // [3,3]
    
    // Матрица трансляции
    set translation[0] = 1.0    // [0,0]
    set translation[1] = 0.0    // [0,1]
    set translation[2] = 0.0    // [0,2]
    set translation[3] = -camX  // [0,3]
    set translation[4] = 0.0    // [1,0]
    set translation[5] = 1.0    // [1,1]
    set translation[6] = 0.0    // [1,2]
    set translation[7] = -camY  // [1,3]
    set translation[8] = 0.0    // [2,0]
    set translation[9] = 0.0    // [2,1]
    set translation[10] = 1.0   // [2,2]
    set translation[11] = -camZ // [2,3]
    set translation[12] = 0.0   // [3,0]
    set translation[13] = 0.0   // [3,1]
    set translation[14] = 0.0   // [3,2]
    set translation[15] = 1.0   // [3,3]
    
    // Комбинируем повороты: Z * X * Y
    call MultiplyMatrix4x4(rotZ, rotX, temp)
    call MultiplyMatrix4x4(temp, rotY, rotation)
    
    // View matrix = Rotation * Translation
    call MultiplyMatrix4x4(rotation, translation, viewMatrix)
endfunction

// Создание матрицы проекции
function CreateProjectionMatrix takes real fovDeg, real screenWidth, real screenHeight, real nearPlane, real farPlane, real array projMatrix returns nothing
    local real aspect = screenWidth / screenHeight
    local real fovRad = fovDeg * 3.141592653589793 / 180.0
    local real f = 1.0 / Tan(fovRad / 2.0)
    local real rangeInv = 1.0 / (nearPlane - farPlane)
    local integer i = 0
    
    // Очищаем матрицу
    loop
        exitwhen i > 15
        set projMatrix[i] = 0.0
        set i = i + 1
    endloop
    
    // Заполняем матрицу проекции
    set projMatrix[0] = f / aspect                                  // [0,0]
    set projMatrix[5] = f                                           // [1,1]
    set projMatrix[10] = (nearPlane + farPlane) * rangeInv         // [2,2]
    set projMatrix[11] = 2.0 * nearPlane * farPlane * rangeInv     // [2,3]
    set projMatrix[14] = -1.0                                      // [3,2]
endfunction

// ОСНОВНАЯ ФУНКЦИЯ: Конвертация мировых координат в экранные
// Параметры:
// worldX, worldY, worldZ - мировые координаты точки
// camX, camY, camZ - позиция камеры
// yawDeg, pitchDeg, rollDeg - углы поворота камеры в градусах
// fovDeg - угол обзора в градусах
// screenWidth, screenHeight - разрешение экрана в пикселях
// nearPlane, farPlane - ближняя и дальняя плоскости отсечения
// Результат: true если точка видна, координаты в udg_ScreenX, udg_ScreenY
function WorldToScreen takes real worldX, real worldY, real worldZ, real camX, real camY, real camZ, real yawDeg, real pitchDeg, real rollDeg, real fovDeg, real screenWidth, real screenHeight, real nearPlane, real farPlane returns boolean
    local real array viewMatrix[16]
    local real array projMatrix[16]
    local real array viewProjMatrix[16]
    
    local real clipX
    local real clipY
    local real clipZ
    local real clipW
    local real ndcX
    local real ndcY
    local real ndcZ
    
    // Создаем матрицы трансформации
    call CreateViewMatrix(camX, camY, camZ, yawDeg, pitchDeg, rollDeg, viewMatrix)
    call CreateProjectionMatrix(fovDeg, screenWidth, screenHeight, nearPlane, farPlane, projMatrix)
    call MultiplyMatrix4x4(projMatrix, viewMatrix, viewProjMatrix)
    
    // Умножение на view-projection матрицу
    set clipX = viewProjMatrix[0] * worldX + viewProjMatrix[1] * worldY + viewProjMatrix[2] * worldZ + viewProjMatrix[3]
    set clipY = viewProjMatrix[4] * worldX + viewProjMatrix[5] * worldY + viewProjMatrix[6] * worldZ + viewProjMatrix[7]
    set clipZ = viewProjMatrix[8] * worldX + viewProjMatrix[9] * worldY + viewProjMatrix[10] * worldZ + viewProjMatrix[11]
    set clipW = viewProjMatrix[12] * worldX + viewProjMatrix[13] * worldY + viewProjMatrix[14] * worldZ + viewProjMatrix[15]
    
    // Проверка, что точка не за камерой
    if clipW <= 0.0 then
        return false
    endif
    
    // Perspective divide
    set ndcX = clipX / clipW
    set ndcY = clipY / clipW
    set ndcZ = clipZ / clipW
    
    // Проверка, что точка в пределах frustum
    if ndcX < -1.0 or ndcX > 1.0 or ndcY < -1.0 or ndcY > 1.0 or ndcZ < -1.0 or ndcZ > 1.0 then
        return false
    endif
    
    // Конвертация в экранные координаты
    set udg_ScreenX = R2I((ndcX + 1.0) * 0.5 * screenWidth)
    set udg_ScreenY = R2I((1.0 - ndcY) * 0.5 * screenHeight)
    set udg_ScreenDepth = ndcZ
    
    return true
endfunction

// Пример использования
function TestWorldToScreenConversion takes nothing returns nothing
    local boolean visible
    
    // Указать параметры камеры( лучше получить динамически актуальные)
    local real cameraX = 0.0
    local real cameraY = 0.0
    local real cameraZ = 100.0
    local real cameraYaw = 0.0      // градусы
    local real cameraPitch = -30.0  // градусы
    local real cameraRoll = 0.0     // градусы
    local real fov = 60.0           // градусы
    
    // Тестируем точку в мире
    set visible = WorldToScreen(100.0, 200.0, 0.0, cameraX, cameraY, cameraZ, cameraYaw, cameraPitch, cameraRoll, fov, 1920.0, 1080.0, 0.1, 1000.0)
    
    if visible then
        call BJDebugMsg("Точка видна на экране: X=" + I2S(udg_ScreenX) + " Y=" + I2S(udg_ScreenY))
    else
        call BJDebugMsg("Точка не видна камере")
    endif
	
endfunction
Ответы (7)
11
darkowlom, спасибо за попытку))
array в параметрах функций нельзя))
И локалки массивы объявлять нельзя)
Код сформирован солянкой)
Что за нейронка? на Deepseek и ChatGPT не похожа )
24
Smeto, Claude 4 sonnet.
Мда, забыл я джазз за 10 лет, оказывается)
Думаю переделать под корректный синтаксис можно, если время появится попробую рефоржед поставить и поколдовать над скриптом, если сам не переделаешь к тому времени
Хотя, еще на балнсе пару баксов есть, можно твой коммент прислать, пусть исправляет)
24
Сейчас почитал, это вроде только массивы vJass нельзя локально объявлять, а стандартные по идее можно - да, надо тестировать самому все-таки, смутно помню что как
11
darkowlom, я последовал твоей идеи, мы снизили хэндлы до 106, но при этом потеряли плавность, так этот вариант не подходит из-за скорости
Может у тебя появятся идеи как это ускорить?
World2Screen
library World2Screen
globals
    real ScreenX
    real ScreenY
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
MoveAbleFrame
library MoveAbleFrame initializer onTriggerInit requires World2Screen

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) // Получаем Z-координату

    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)
                    call moveFrame(ScreenX,ScreenY)
                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
        //call show(false, GetTriggerPlayer())
    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
    if flag == true then
        call moveFrame(ScreenX,ScreenY)
    endif
endfunction

function CoordsMouse takes nothing returns nothing
    local real x = BlzGetTriggerPlayerMouseX()
    local real y = BlzGetTriggerPlayerMouseY()
    local real z
    if flag == true then
        call BJDebugMsg("CoordsMouse")
        call MoveLocation(moveLoc, x, y)
        set z = GetLocationZ(moveLoc) // Получаем Z-координату
        call World2Screen(x,y,z)
        call moveFrame(ScreenX,ScreenY)
    endif
endfunction

function onTriggerInit takes nothing returns nothing
local integer playerIndex = 0
local timer Timer = 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.01, true, function update2)
endfunction

endlibrary

24
Smeto, я сейчас практически переписал код на рабочий вариант, но у меня патч 1.26а с вайдскрин фиксом - поэтому я конвертирую позицию юнита в экранные координаты и пока у меня есть погрешность. Надо качать рефоржед и проверять там, если можно то вышли в личку эту тормозящую версию -я поставлю и попробую ускорить.
На вскидку: можно не получать ротаторы камеры каждую конверсию, а писать их в переменные при изменении камеры, только вопрос: можно ли в рефоржед отловить нажатия клавиш поворота камеры и прокрутку колеса мыши, что бы при этих действиях тоже обновлять?
24
Вообще классический фрозентрон хранит позицию мыши в двух переменных по адресам 6FACE690 для X и 6FACE694 для Y и их можно напрямую дернуть мемхаком, а вот какая архитектура в рефоржеде и можно ли извратиться для получения куска памяти я не знаю - никогда не пробовал его дизассемблировать
P.S множители 0.4 и 0.355, если память не изменяет это для 16/9 на других соотношениях поплывет, если получить разрешение напрямую нельзя, то стоит запрашивать их у игрока принудительно и пока не введет в игру не пускать
24
Да однозначно надо рефоржед ставить - уже в процессе. Проверил твою функцию в 1.26а - в старом патче нулевые координаты это левый верхний угол, по твоему коду получается левый нижний и такая же погрешность как и у меня, она скорее всего из-за широкоформатного фикса.
А не, это не погрешность видимо в рефоржед возможны отрицательные координаты мыши типа -0.124 в левой стороне экрана и оси местами поменяли, когда в старом патче меньше 0 числа невозможны
скрин
Загруженные файлы
Этот комментарий удален
11
Корректнее будет пересчитать мировые координаты
Блин сама идея крутая. Токо вот работать это будет только с видом сверху , от третьего лица или от первого так не реализуешь. Текущее решение 6-ти летней давности, Самореклама.
Ответы (4)
24
Корректнее будет пересчитать мировые координаты
Блин сама идея крутая. Токо вот работать это будет только с видом сверху , от третьего лица или от первого так не реализуешь.
Реализуешь - считаешь по матрицам трансформации и все, даже при упрощенном варианте Smeto в горизонтальной камере сейчас всего на сантиметр вверх сносит кнопку, это корректируется без проблем, можно даже костылем. Там другая проблема у нас теперь - задержка в событии регистрации движения мыши
11
darkowlom, да, задержка составляет если не ошибаюсь в 0.15 сек
11
darkowlom, интересно, но не совсем понимаю каким образом, если смотреть в небо.
24
darkowlom, интересно, но не совсем понимаю каким образом, если смотреть в небо.
С небом могут быть проблемки, надо будет потестировать, но это можно решить невидимым мостом перед камерой - его игра на уровне ядра считает террейном и, кстати, юнитом тоже
24
Сетка отъедает много памяти, но может будет меньше если ее напрямую в fdf запихать из каких-то очень простых фреймов - надо ставить эксперементы и замерять потребление ресурсов. Я впервые в жизни редактор рефоржа сегодня открыл вообще)
11
его игра считает террейном
Ну тогда это же будут координаты разрушаемого объекта или проекции на землю? Это с любым разрушаемым объектом работает? А что если смотреть по линии горизонта и упираться в стену? Там же координаты высоты нету у мышки. Чет много вопросов. Но тема меня заинтересовала 🤓
Ответы (2)
24
Koladik, Координаты по Z можно получить через локацию, посмотри как это реализовано выше в коде или более наглядно в моей карте RPGEngine, в моих ресурсах есть - там через невидимые мосты сделана система запрыгивания на декорации и приближение камеры когда объект за спиной ее загораживает, это под 1.26a. Там мосты по сути заменяют меш коллизии
Работает с РО на которые можно залезть, обычно это мосты
11
под 1.26a
Да под 1.26 с мем хаком мне кажется проблем отслеживать мышку вообще не должно быть.
11
Вышла новая версия! Прокрутить к ресурсу
Уменьшил количество Хэндлов до 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
Ответы (1)
24
Smeto, по-возможности сделай версию, которая будет открываться сразу на 2.0, для совместимости и удобства пользователей
P.S продолжаю эксперименты в попытках обойти задержку события, может что-то накопаю
11
Вышла новая версия! Прокрутить к ресурсу
Добавил версию карты которая открывается на Warcraft 3 Reforged 2.0.0
Скачать версию карты для Warcraft 3 Reforget 2.0.0
Загруженные файлы
Чтобы оставить комментарий, пожалуйста, войдите на сайт.