so while building a mobile game with Corona SDK i am encountering some problems now and then. One of them I didn't seem to solve :
When spawning display objects in a loop, there seems to randomly appear a positional difference between two of the objects in a row.
At first, I thought this was due to large chunks of code that were executed between the actual spawning and the start of the transition, but then I managed to reproduce the same problem in few lines :
local rectangleLoopTimer;
local counter = 0;
local rectangleArray = {}
local function rectangleLoop()
counter = counter + 1
local thisRectangle = display.newRect(1, 1, 216, 400)
thisRectangle.anchorX = 0
table.insert(rectangleArray, thisRectangle)
transition.to(
thisRectangle,
{
time = 5000,
x = thisRectangle.x + 1080,
onComplete = function()
display.remove(thisRectangle)
table.remove(rectangleArray, counter)
end
}
)
end
rectangleLoopTimer = timer.performWithDelay(985, rectangleLoop, 0)
If one executes this, then one sees what I mean, so what do you think why this happens? I appreciate every answer!
Greetings, Nils
EDIT:
This also produces the same problem :
local rectangleLoopTimer;
local counter = 0
local rectangleArray = {}
local thisRectangle
local function rectangleLoop()
counter = counter + 1
thisRectangle = display.newRect(1, 1, 216, 400)
thisRectangle.anchorX = 0
thisRectangle.lastTime = 0
thisRectangle.rate = 216
table.insert(rectangleArray, thisRectangle)
thisRectangle.lastTime = system.getTimer()
thisRectangle.enterFrame = function(self, event)
local curTime = system.getTimer()
local dt = curTime - self.lastTime
self.lastTime = curTime
local dx = self.rate * dt / 1000
self.x = self.x + dx
end
Runtime:addEventListener("enterFrame", thisRectangle)
end
rectangleLoopTimer = timer.performWithDelay(1000, rectangleLoop, 0)
RE-EDIT:
This code also produces the same problem, albeit using framerate independent animation. The issue is getting emphasized when increasing the speed of the loop as in the code below :
local loopSpeed = 306
local loopTimerSpeed = 1000
local gapTable = {}
local gapLoopTimer
local frameTime
local gap
--enterFrame for time only
local function frameTime(event)
frameTime = system.getTimer()
end
--enterFrame
local function enterFrame(self, event)
local deltaTime = frameTime - self.time
print(deltaTime/1000)
self.time = frameTime
local speed = self.rate * deltaTime / 1000
self:translate(speed, 0)
end
--loop speed function
local function setLoopSpeed(factor)
loopSpeed = loopSpeed * factor
loopTimerSpeed = loopTimerSpeed / factor
end
--set the loop speed
setLoopSpeed(3)
--loop to create gaps
local function createGap()
gap = display.newRect(1, 1, 308, 442)
gap.time = system.getTimer()
gap.anchorX = 1
gap.anchorY = 0
--animation
gap.rate = loopSpeed
gap.enterFrame = enterFrame
Runtime:addEventListener("enterFrame", gap)
--fill table for cleaning up
table.insert(gapTable, gap)
--cleaning up
for i = #gapTable, 1, -1 do
local thisGap = gapTable[i]
if thisGap.x > display.contentWidth + 500 then
display.remove(thisGap)
table.remove(gapTable, i)
Runtime:removeEventListener("enterFrame", thisGap)
end
thisGap = nil
end
end
Runtime:addEventListener("enterFrame", frameTime)
gapLoopTimer = timer.performWithDelay(
loopTimerSpeed,
createGap,
0
)
This is a very common problem with transitions, and [to me] a bug in Corona SDK.
The important thing to note is how transitions work.
Transitions are nothing else than a table with references to objects and information about what should be done to them each frame.
Each frame such object is retrieved and current time is used to calculate the difference that should be applies to the values of the object, as specified in the transition itself.
This basically means that if you ask Corona to move an object from x = 0
to x = 100
in time = 100
. Each frame, Corona will take that information, take current time, and will calculate the x
value of your object.
The issue here is, that the current time taken, is current time at a time of calculation, and not time of the frame. It means, that if you have a lot of transitions, it could be quite a few milliseconds between first and last of the transitions within one frame. This will result in different positions within same frame.
If Corona would take frame time [so time at the beginning of the frame] it would use the same value to calculate everything, and no matter how many objects you would be transitioning from A to B, all of them would appear in the same place in all of the frames.
The easiest way to fix this, would be to handle transitions manually in enterFrame
or use a library which does it for you, for example: AKTween.
Hope this helps.
EDIT: Based on your additional code and comments, I think this should work as you wanted. Please forgive me the code quality, I wrote it from memory and didn't test it in Corona.
local rectangleLoopTimer;
local allRectangles = display.newGroup()
local lastTime = system.getTimer()
local function enterFrame()
local curTime = system.getTimer()
local dt = curTime - lastTime
lastTime = curTime
for i = allRectangles.numChildren, 1 do
local rect = allRectangles[i]
local dx = rect.rate * dt / 1000
rect.x = rect.x + dx
end
end
Runtime:addEventListener("enterFrame", enterFrame)
local function createRectangle()
local thisRectangle = display.newRect(1, 1, 216, 400)
thisRectangle.anchorX = 0
thisRectangle.lastTime = 0
thisRectangle.rate = 216
allRectangles:insert(thisRectangle)
end
timer.performWithDelay(1000, createRectangle, 0)
EDIT AFTER RE-EDIT of the post:
You have time set in enterFrame
listener, but you don't actually know when it's going to be called. I would not count on the order of functions called during enterFrame
stage.