lua - Corona Lua 将对象 y 属性显示为百万分之三像素(!)

标签 lua box2d coronasdk game-physics

当我运行此代码(基于“DragMe”示例应用程序)时,我在非常特殊的情况下得到了非常意外的结果。

我的“snapToGrid”函数在拖动后将 x 和 y 设置为最接近的 100 舍入值。

“检查”函数在创建、移动或旋转对象(通过单击对象)后输出 x 和 y 值。

如果将一个对象放置在第 5 行(因此 y = 500)并旋转它,您将看到 y 值更改为 499.99996948242,但在任何其他行中都不会发生这种情况。

这该如何解释呢?我注意到如果物理体没有添加到显示对象中,这种情况就不会发生。

我知道我可以在旋转后调用 snapToGrid 或在将值用于其他用途之前调用 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中没有整数,所以所有数字都是双浮点值。根据FloatingPoint on Lua wiki ,

Some vendors' printf implementations may not be able to handle printing floating point numbers accurately. Believe it or not, some may incorrectly print integers (that are floating point numbers). This can manifest itself as incorrectly printing some numbers in Lua.

确实,请尝试以下操作:

for i=1,50,0.01 do print(i) end

您将看到许多数字的打印结果完全符合您的预期,其中许多打印的错误为 10^-12 甚至 2 x 10^-12。

但是,当您不将其提供给物理模块时,您的 x 打印效果很好。因此,物理模块肯定会对物体位置进行一些计算并改变它。我当然希望对于动态对象(由于碰撞检测),但这里你的对象似乎是“静态”的。所以即使对于静态物体,物理学也一定会调整 x 。调整是如此之小,以至于在屏幕上看不到(您无法感知任何小于像素的运动),但您是对的,问为什么很有趣。

SO帖子Lua: subtracting decimal numbers doesn't return correct precision值得一读;它有一些您可能会感兴趣的链接,并给出了一个巧妙的例子,即十进制 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 的误差很小,所以从位数上看它们并不相同;尽管在打印时,差异太小而无法显示。您可能想阅读Floating Point Guide .

关于lua - Corona Lua 将对象 y 属性显示为百万分之三像素(!),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22734144/

相关文章:

multithreading - Lua 脚本协程

lua_x在不同的lua状态之间移动

字符串到数字的转换

android - 发生碰撞时移除物体

java - 删除后接触处理程序中的 Box2D 空夹具

opengl - 使用 premake4 构建项目文件 - 如何?

box2d: GetLinearVelocity() 检测运动?

android - 如何使用 Corona SDK 将分享功能添加到 Android 应用程序?

android - 如何在触摸时删除克隆

events - 在 Corona SDK 中对象的 alpha 为 0 时获取触摸事件