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