Corona Lua以三百万分之一的像素显示对象y属性(!)

时间:2014-03-29 17:12:15

标签: lua box2d corona game-physics

当我运行此代码(基于“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)

2 个答案:

答案 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论坛上问过并得到了这个答案。

http://forums.coronalabs.com/topic/46245-display-object-that-has-static-physics-body-moves-very-slightly-when-rotated/