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
问题分析
- 坐标参考系不统一:克隆棋子的父级是
frame.Parent.Parent,而原棋子最终父级是目标格子Frame,两者的绝对坐标参考系不同,导致补间目标位置计算错误 - 布局时机问题:在补间前就将原棋子移到目标格子,
UIGridLayout的布局更新存在延迟,此时获取的newAbsPos并非最终布局后的正确位置 - 绝对坐标误用:直接用屏幕绝对坐标设置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
相关产品推荐
相关产品推荐

