当我运行此代码(基于"DragMe"示例应用程序)时,在非常特殊的情况下,我得到了非常意外的结果。
我的"snapToGrid"函数将 x 和 y 设置为拖动后最接近的整数 100 值。
"检查"功能在创建、移动或旋转对象(通过单击它)后输出 x 和 y 值。
如果将对象放在第 5 行(因此 y = 500)并旋转它,您将看到 y 值更改为 499.99996948242,但这不会发生在任何其他行中。
这怎么解释呢? 我注意到,如果没有将物理实体添加到显示对象中,则不会发生这种情况。
我知道我可以在旋转后调用 snapToGrid,或者在将其用于其他任何事情之前对值进行舍入,但我认为这里有一个重要的机会让我学习一些关于 Corona 的有用知识。
local function snapToGrid(t)
modHalf = t.x % t.width
if modHalf > t.width/2 then
t.x = t.x + (t.width-modHalf)
end
if modHalf < t.width/2 then
t.x = t.x - modHalf
end
modHalfY = t.y % t.height
if modHalfY > t.height/2 then
t.y = t.y + (t.height-modHalfY)
end
if modHalfY < t.height/2 then
t.y = t.y - modHalfY
end
display.getCurrentStage():setFocus( nil )
t.isFocus = false
return true
end
function rotatePiece(target)
if target.rotation == 270 then
target.rotation = 0
else
target.rotation = target.rotation + 90
end
end
local function dragBody(event)
local target = event.target
local phase = event.phase
local halfWidth = target.width/2
local halfHeight = target.height/2
--get tileX and tileY relative to tile centre
local tileX = event.x-target.x
local tileY = event.y-target.y
local modHalf = ""
local snap = 15
if phase == "began" then
-- Make target the top-most object
display.getCurrentStage():setFocus( target )
-- Spurious events can be sent to the target, e.g. the user presses
-- elsewhere on the screen and then moves the finger over the target.
-- To prevent this, we add this flag. Only when it's true will "move"
-- events be sent to the target.
target.isFocus = true
-- Store initial position
target.x0 = event.x - target.x
target.y0 = event.y - target.y
elseif target.isFocus then
if phase == "moved" then
-- Make object move (we subtract target.x0,target.y0 so that moves are
-- relative to initial grab point, rather than object "snapping").
target.x = event.x - target.x0
target.y = event.y - target.y0
if target.x > display.contentWidth - (target.width/2) then target.x = display.contentWidth - (target.width/2) end
if target.y > display.contentHeight - (target.height/2) then target.y = display.contentHeight - (target.height/2) end
if target.x < 0 + (target.width/2) then target.x = 0 + (target.width/2) end
if target.y < 0 + (target.height/2) then target.y = 0 + (target.height/2) end
modHalf = target.x % target.width
if modHalf < snap then
target.x = target.x - modHalf
end
if modHalf > ((target.width) - snap) then
target.x = target.x + ((target.width)-modHalf)
end
modHalfY = target.y % target.height
if modHalfY < snap then
target.y = target.y - modHalfY
end
if modHalfY > ((target.height) - snap) then
target.y = target.y + ((target.height)-modHalfY)
end
hasMoved = true
return true
elseif phase == "ended" then
if hasMoved then
hasMoved = false
snapToGrid(target)
--tile has moved
examine(target)
return true
else
--rotate piece
rotatePiece(target)
display.getCurrentStage():setFocus( nil )
target.isFocus = false
--tile has rotated
examine(target)
return true
end
end
end
-- Important to return true. This tells the system that the event
-- should not be propagated to listeners of any objects underneath.
return true
end
local onTouch = function(event)
if event.phase == "began" then
local tile = {}
img = display.newRect(event.x,event.y,100,100)
img:addEventListener( "touch", dragBody )
snapToGrid(img)
--top right corner and top middle solid
topRight = {16,-16,16,50,50,50,50,-16}
--top left and left middle solid
topLeft = {-16,-16,-16,-50,50,-50,50,-16}
--bottom right and right middle solid
bottomRight = {16,16,16,50,-50,50,-50,16}
--bottom left and bottom middle solid
bottomLeft = {-16,16,-16,-50,-50,-50,-50,16}
physics.addBody( img, "static",
{shape=topRight},
{shape=topLeft},
{shape=bottomLeft},
{shape=bottomRight}
)
--new tile created
examine(img)
return true
end
end
function examine(img)
print("--------------------------------------------------")
if img ~= nil then
print("X: "..img.x..", Y: "..img.y)
end
print("--------------------------------------------------")
end
local img
local physics = require( "physics" )
physics.setDrawMode( "hybrid" )
--draw gridlines
for i = 49, display.contentHeight, 100 do
local line = display.newLine(0,i,display.contentWidth,i)
local line2 = display.newLine(0,i+2,display.contentWidth,i+2)
end
for i = 49, display.contentWidth, 100 do
local line = display.newLine(i,0,i,display.contentHeight )
local line2 = display.newLine(i+2,0,i+2,display.contentHeight )
end
--init
physics.start()
Runtime:addEventListener("touch", onTouch)
有几种可能性。首先,由于Lua中没有整数,因此所有数字都是双精度浮点值。根据Lua维基上的FloatingPoint,
某些供应商的 printf 实现可能无法处理 准确打印浮点数。信不信由你,一些 可能会错误地打印整数(即浮点数)。这 可以表现为在 Lua 中错误地打印某些数字。
实际上,请尝试以下操作:
for i=1,50,0.01 do print(i) end
您将看到很多数字的打印完全符合您的预期,许多数字打印时错误为 10^-12 甚至 2 x 10^-12。
但是,当您不将其提供给物理模块时,您的x
打印正常。因此,物理模块肯定会对物体位置进行一些计算并对其进行更改。对于动态对象(由于碰撞检测),我当然希望如此,但在这里您的对象似乎是"静态的"。因此,即使对于静态物体,物理学也必须调整x。调整是如此之小,以至于在屏幕上看不到(您无法感知任何小于像素的运动),但您是对的,问为什么很有趣。
SO post Lua:减去十进制数不会返回正确的精度值得一读;它有一些你可能会觉得有趣的链接,并给出了一个简洁的例子,十进制0.01不能以2为底精确地表示;就像1/3不能以10为底表示(但它可以用3为底:它将是0.1个基数3!)。这个问题表明,尽管Lua(或者可能是底层的C)足够聪明,可以将0.01打印为0.01,但它无法将10.08-10.07打印为0.01。
如果你真的想动摇你对浮点值的理解,试试这个:
> a=0.3
> b=0.3
> print(a==b)
true
> -- so far so good; now this:
> a=0.15 + 0.15
> b=0.1 + 0.2
> print(a,b)
0.3 0.3
> print(c==d)
false -- woa!
这可以通过以下事实来解释:二进制中的 0.15 有一个小误差,与 0.1 或 0.2 的误差不同,因此就位而言,它们并不相同;尽管在打印时,差异太小而无法显示。您可能需要阅读浮点指南。
当您添加物理特性时,才会出现此问题,因此我认为可以公平地说,该问题是由将控制权转移到box2d引起的,而不是仅由Corona处理数字引起的。 我在科罗纳论坛上问过并得到了这个答案。
http://forums.coronalabs.com/topic/46245-display-object-that-has-static-physics-body-moves-very-slightly-when-rotated/