You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Roblox Studio中Luau UI Tween定位错误问题求助

UI棋子平滑移动定位偏差修复方案

问题现状

  • 前期问题:棋子在补间过程中完全消失,直到动画结束才显示
  • 当前问题:简化代码后,补间动画结束时,克隆棋子的位置与目标格子内的原棋子位置偏下不符
  • 需求:实现无玩家输入的棋子跨格子平滑移动,代码需清晰易修改,后续要支持先上后下的移动轨迹
  • 场景:棋盘由带UIGridLayout的黑白格子Frame构成,用于避免棋子碰撞

原代码

local SGUI = game.StarterGui
local frame = SGUI.ScreenGui.Background.OutlineFrame.InnerFrame
local theDock = frame.Parent.Parent.PlayerDock
local thePawn = theDock.Player1
local currentBoxOfPawn = thePawn.CurrentBox.Value
local ts = game:GetService("TweenService")

local isMoving = thePawn.Moving

if isMoving.Value then return end

if currentBoxOfPawn == "000" then
    currentBoxOfPawn = "001"

    local oldPos = thePawn.Position
    local oldSize = thePawn.Size

    isMoving.Value = true
    thePawn.Visible = false

    local newPawn = thePawn:Clone()
    newPawn.Visible = true
    newPawn.Parent = frame.Parent.Parent -- Set to frame.Parent to keep it visible

    local function getAbsolutePositionAndSize(guiObject)
        local absPos = guiObject.AbsolutePosition
        local absSize = guiObject.AbsoluteSize
        return UDim2.new(0, absPos.X, 0, absPos.Y), UDim2.new(0, absSize.X, 0, absSize.Y)
    end

    local oldAbsPos, oldAbsSize = getAbsolutePositionAndSize(thePawn)
    newPawn.Position = oldAbsPos
    newPawn.Size = oldAbsSize

    -- Ensure thePawn is parented to the correct frame
    local newParent = frame:FindFirstChild(currentBoxOfPawn)
    if newParent then
        thePawn.Parent = newParent
    else 
        isMoving.Value = false
        return
    end

    local newAbsPos, newAbsSize = getAbsolutePositionAndSize(thePawn)
    
    local movement = ts:Create(newPawn, TweenInfo.new(1, Enum.EasingStyle.Linear, Enum.EasingDirection.InOut), {
        Position = newAbsPos,
        Size = newAbsSize
    })
    movement:Play()

    movement.Completed:Connect(function()
        isMoving.Value = false
        thePawn.Visible = true
        newPawn:Destroy()
    end)
end

问题分析

  1. 坐标参考系不统一:克隆棋子的父级是frame.Parent.Parent,而原棋子最终父级是目标格子Frame,两者的绝对坐标参考系不同,导致补间目标位置计算错误
  2. 布局时机问题:在补间前就将原棋子移到目标格子,UIGridLayout的布局更新存在延迟,此时获取的newAbsPos并非最终布局后的正确位置
  3. 绝对坐标误用:直接用屏幕绝对坐标设置Position属性,当UI元素的父容器不是ScreenGui时,Position的参考系是父容器而非屏幕,必然导致位置偏移

修正后的代码

local SGUI = game.StarterGui
local screenGui = SGUI.ScreenGui
local boardFrame = screenGui.Background.OutlineFrame.InnerFrame
local playerDock = screenGui.PlayerDock
local thePawn = playerDock.Player1
local isMoving = thePawn.Moving
local ts = game:GetService("TweenService")

-- 防重复触发
if isMoving.Value then return end

local currentBoxId = thePawn.CurrentBox.Value
if currentBoxId == "000" then
    local targetBoxId = "001"
    local targetBox = boardFrame:FindFirstChild(targetBoxId)
    
    -- 检查目标格子是否存在
    if not targetBox then return end

    isMoving.Value = true
    thePawn.Visible = false

    -- 1. 创建克隆棋子,父级设为ScreenGui,确保坐标参考系统一
    local tweenPawn = thePawn:Clone()
    tweenPawn.Visible = true
    tweenPawn.Parent = screenGui
    -- 初始位置设为原棋子的绝对位置(相对于ScreenGui)
    tweenPawn.Position = UDim2.fromOffset(thePawn.AbsolutePosition.X, thePawn.AbsolutePosition.Y)
    tweenPawn.Size = UDim2.fromOffset(thePawn.AbsoluteSize.X, thePawn.AbsoluteSize.Y)

    -- 2. 计算目标位置:目标格子的绝对位置(相对于ScreenGui)
    local targetAbsPos = targetBox.AbsolutePosition
    local targetAbsSize = targetBox.AbsoluteSize
    -- 如果格子内有UIGridLayout,需考虑布局后的棋子位置偏移
    local gridLayout = targetBox:FindFirstChildOfClass("UIGridLayout")
    local finalTargetPos = UDim2.fromOffset(targetAbsPos.X, targetAbsPos.Y)
    if gridLayout then
        -- 取UIGridLayout的CellPadding和CellSize,计算棋子在格子内的正确位置
        local paddingX = gridLayout.CellPadding.X.Offset
        local paddingY = gridLayout.CellPadding.Y.Offset
        finalTargetPos = UDim2.fromOffset(
            targetAbsPos.X + paddingX/2,
            targetAbsPos.Y + paddingY/2
        )
    end

    -- 3. 执行补间动画
    local tweenInfo = TweenInfo.new(
        1, 
        Enum.EasingStyle.Linear, 
        Enum.EasingDirection.InOut
    )
    -- 如需先上后下的轨迹,可在此处拆分补间(先到中间点,再到目标点)
    local movementTween = ts:Create(tweenPawn, tweenInfo, {
        Position = finalTargetPos,
        Size = UDim2.fromOffset(targetAbsSize.X, targetAbsSize.Y)
    })
    movementTween:Play()

    -- 4. 动画完成后的收尾
    movementTween.Completed:Connect(function()
        -- 将原棋子移到目标格子,利用UIGridLayout自动布局
        thePawn.Parent = targetBox
        thePawn.Visible = true
        -- 更新棋子的CurrentBox记录
        thePawn.CurrentBox.Value = targetBoxId
        -- 清理克隆棋子
        tweenPawn:Destroy()
        isMoving.Value = false
    end)
end

代码优化点说明

  • 统一坐标参考系:将克隆棋子的父级设为ScreenGui,所有位置计算都基于屏幕绝对坐标,避免父容器切换带来的偏移
  • 延迟原棋子移动:动画结束后再将原棋子移到目标格子,确保UIGridLayout完成布局,原棋子位置准确
  • 兼容UIGridLayout:自动检测格子内的布局组件,计算棋子在格子内的正确偏移位置
  • 预留轨迹扩展:注释标记了添加先上后下轨迹的位置,只需拆分补间为两步即可实现(先移动到上方中间点,再移动到目标点)

内容的提问来源于stack exchange,提问作者Hamoodi youtube

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 15:30:19