当我运行此代码(基于“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)
答案 0 :(得分:1)
有几种可能性。首先,由于Lua中没有整数,所有数字都是双浮点值。根据{{3}},
一些供应商&#39; printf实现可能无法处理 准确打印浮点数。不管你信不信,有些人 可能会错误地打印整数(即浮点数)。这个 可以表现为在Lua中错误地打印一些数字。
确实,请尝试以下方法:
for i=1,50,0.01 do print(i) end
您会看到很多数字的打印方式与您预期的完全一样,许多打印的误差为10 ^ -12甚至2 x 10 ^ -12。
但是,当您不将物品模块提供给物理模块时,您的x
打印效果很好。所以物理模块肯定会对物体位置做一些计算并改变它。我当然希望对于动态对象(由于碰撞检测),但在这里你的对象似乎是&#34;静态&#34;。因此,即使对于静态对象,物理也必须调整x。调整非常小,以至于无法在屏幕上看到(你无法感知任何小于一个像素的运动),但你说得对,有趣的是,为什么这么做。
SO帖子FloatingPoint on Lua wiki值得一读;它有一些你可能会感兴趣的链接,并给出了一个简洁的例子,即十进制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不同的小误差,因此就位而言它们不相同;虽然在印刷时,差异太小而无法显示。您可能需要阅读Lua: subtracting decimal numbers doesn't return correct precision。
答案 1 :(得分:1)
实际上只有在添加物理时才会出现这个问题,所以我认为可以说这个问题是由于将控制转移到box2d引起的,而不是仅通过Corona处理数字。我在Corona论坛上问过并得到了这个答案。