I am using timer.performWithDelay to time how long it takes a player to complete a level. I want it to measure down to the 100th of a second (because the game is multiplayer, and I don't want there to be too many ties).
Here is what I did:
local totaltime = 0
local function counter()
totaltime = totaltime + 0.01
print(totaltime)
end
timer1 = timer.performWithDelay( 10, counter, 0)
It results in each "second" lasting about 4 seconds. Is this just not practical or is there a flaw somewhere?
When timer.preformWithDelay is given a time delay smaller then the time between your frames the timer will wait until the next frame is entered to call the function.
That means if you have a game running at 30 or 60 fps, you would have a 'frame ms' of about 16 or 33ms. So the minimum delay you can put is the delay between your frames.
In your case you want to set your timer every 1/100th of a second, or with 10ms. This means, since your frame is most likely 16ms (60fps), that every logged 10ms you are actually waiting an addional 6ms.
Now you could solve this if you ran with 100 FPS and thus achieved said 10 ms, but this is NOT recommendable.
AlanPlantPot provided the answer for following solution on coronaLabs:
I would use the enterFrame function instead. Your timer won't go up in single milliseconds (it will increase by however many ms have passed in each frame), but nobody would be able to read that fast anyway.
local prevFrameTime, currentFrameTime --both nil
local deltaFrameTime = 0
local totalTime = 0
local txt_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 )
txt_counter.x = 150
txt_counter.y = 288
txt_counter:setTextColor( 255, 255, 255 )
group:insert( txt_counter )
and
local function enterFrame(e)
local currentFrameTime = system.getTimer()
--if this is still nil, then it is the first frame
--so no need to perform calculation
if prevFrameTime then
--calculate how many milliseconds since last frame
deltaFrameTime = currentFrameTime - prevFrameTime
end
prevFrameTime = currentFrameTime
--this is the total time in milliseconds
totalTime = totalTime + deltaFrameTime
--multiply by 0.001 to get time in seconds
txt_counter.text = totalTime * 0.001
end
Related
I'm trying to make a part of a program that's a timer through two loops. The timer will repeat a certain number of times (the it variable) hence the first loop. The second loop is the actual timer. For a certain number of seconds, every second it updates the text of a text object. The problem I face is that when I print the text of the text object to the console, it updates, but the text won't change on the screen itself until all the loops are done. Any help with explanation or anything to help guide me in the right direction would be very much appreciated.
Here is the code, don't worry about the unused parameters:
local function sleep(s)
local ntime = os.clock() + s/10
repeat until os.clock() > ntime
end
local function watch(mode, it, text, texty, para)
local text = display.newText(scene.view, "",
display.contentCenterX, 200, nativeSystemFont, 60)
text:setFillColor(0,0,256)
local i = 0
local sec = 0
local goal = 20
text.text = sec
while (i < it) do
sec = 0
while (sec < goal) do
sec = sec + 1
print(text.text)
text.text = sec
sleep(10)
end
i = i + 1
end
end
In a game I'm making, there's a square where the game is happening inside. I want the circles that spawn in to start at the top and travel down to the bottom. In the original program, they travel from left to right. How would I go about this? I don't know how to get it to start at the top and travel down instead of starting at the left and traveling to the right.
Here's the original code (I know it's a lot, sorry). I am trying to help a friend.
--Made by Joms or /u/jomy582
--Please credit me if using this in a battle.
spawntimer = 0
timerspawn = 0
storedtime = Time.time
bullets = {}
bulletsbig = {}
bulletswarn = {}
dir = 1
bigheight = {33, 98}
function Update()
spawntimer = spawntimer + (Time.dt*Time.mult)
timerspawn = timerspawn + (Time.dt*Time.mult)
--normal bullets
--change the number in the if statement to make them spawn more frequently/less frequently
--EX: spawntimer > 0.2 spawns them pretty fast
--EX2: spawntimer > 1 spawns them pretty slow
--Make sure to change the subtraction method in the if statement
if spawntimer > 0.16 then
spawntimer = spawntimer-0.16
local bullet = CreateProjectile("bullet", -Arena.width/2, math.random(-Arena.height/2, Arena.height/2))
bullet.SetVar("deadly", true)
table.insert(bullets, bullet)
end
--warning. spawns a warning every 5 seconds
--You could change it, but that may cause some bugs
if timerspawn > 2.2 then
timerspawn = timerspawn-2.2
dir = math.random(1,2)
local bulletwarn = CreateProjectile("warning", 0, Arena.height/2 - bigheight[dir])
bulletwarn.SetVar("warningtimer", 0)
bulletwarn.SetVar("soundtimer", 0)
bulletwarn.SetVar("animtimer", 0)
bulletwarn.SetVar("anim", 1)
table.insert(bulletswarn, bulletwarn)
end
--controlling normal bullets
--a simple method that moves the bullets 1 pixel each frame
--you can change it by editing the bullet.move
--EX: bullet.Move(5*Time.mult, 0) moves the bullets 5 pixels each frame
for i=1, #bullets do
local bullet = bullets[i]
if bullet.isactive then
bullet.Move(2*Time.mult, 0)
if bullet.y > -Arena.height/2 then
bullet.Remove()
end
end
end
--controlling warning timer
--a method that controls the warning timer
for i=1, #bulletswarn do
local bullet = bulletswarn[i]
if bullet.isactive then
local warningtimer = bullet.GetVar("warningtimer") + (Time.mult*Time.dt)
local soundtimer = bullet.GetVar("soundtimer") + (Time.mult*Time.dt)
local animtimer = bullet.GetVar("animtimer") + (Time.mult*Time.dt)
local bulletSprite = bullet.sprite
local anim = bullet.GetVar("anim")
--flashing colors
--change the animtimer > TIME where time is how often you want the warning to blink
--warnings last for 3 seconds. Can change that as well
--to get different colors, find the rgb value of the color you want and insert them below
if animtimer > 0.08 then
animtimer = animtimer-0.08
if anim == 1 then
local r = 0 --put Red value here
local g = 0 --put Green value here
local b = 169 --put Blue value here
bulletSprite.color = {r/255, g/255, b/255} -- changes the color to whatever you'd like. Uses RGB.
bullet.SetVar("anim", 2)
elseif anim == 2 then
local r = 0 --put Red value here
local g = 0 --put Green value here
local b = 255 --put Blue value here
bulletSprite.color = {r/255, g/255, b/255} -- changes the color to whatever you'd like. Uses RGB.
bullet.SetVar("anim", 1)
end
end
--plays a timer evert 10 frames for 3 seconds.
--change the soundname to change the sound
--change the soundtimer > 0.16 to change how often it plays
--can change how long it lasts as well by changing the less than statement
if soundtimer > 0.10 then
soundtimer = soundtimer-0.10
Audio.PlaySound("alarm")
end
--this controls when to spawn the bullets
--change the statement to change how long the warning timer is
if warningtimer > 2 then
warningtimer = warningtimer-2
Audio.PlaySound("shoot")
local bullet1 = CreateProjectile("leftbigbullet", Arena.width/2+30, Arena.height/2 - bigheight[dir]) --where to spawn them
bullet1.SetVar("speed", -4) --how fast
bullet1.SetVar("deadly", true)
local bullet2 = CreateProjectile("rightbigbullet", -Arena.width/2-30, Arena.height/2 - bigheight[dir]) --where to spawn them
bullet2.SetVar("speed", 4) --how fast
bullet2.SetVar("deadly", true)
table.insert(bulletsbig, bullet1)
table.insert(bulletsbig, bullet2)
bullet.Remove()
end
bullet.SetVar("warningtimer", warningtimer)
bullet.SetVar("animtimer", animtimer)
bullet.SetVar("soundtimer", soundtimer)
end
end
--controlling big bullets
--this method controls the big bullets
for i=1, #bulletsbig do
local bullet = bulletsbig[i]
if bullet.isactive then
local speed = bullet.GetVar("speed")
bullet.SendToBottom()
bullet.Move(speed*Time.mult, 0)
if bullet.absx > 700 or bullet.absx < -70 then
bullet.Remove()
end
end
end
end
function OnHit(bullet)
if(bullet.getVar("deadly")) then
Player.Hurt(3)
end
end
I am using timer.performWithDelay to time how long it takes a player to complete a level. I want it to measure down to the 100th of a second (because the game is multiplayer, and I don't want there to be too many ties).
Here is what I did:
local totaltime = 0
local function counter()
totaltime = totaltime + 0.01
print(totaltime)
end
timer1 = timer.performWithDelay( 10, counter, 0)
It results in each "second" lasting about 4 seconds. Is this just not practical or is there a flaw somewhere?
When timer.preformWithDelay is given a time delay smaller then the time between your frames the timer will wait until the next frame is entered to call the function.
That means if you have a game running at 30 or 60 fps, you would have a 'frame ms' of about 16 or 33ms. So the minimum delay you can put is the delay between your frames.
In your case you want to set your timer every 1/100th of a second, or with 10ms. This means, since your frame is most likely 16ms (60fps), that every logged 10ms you are actually waiting an addional 6ms.
Now you could solve this if you ran with 100 FPS and thus achieved said 10 ms, but this is NOT recommendable.
AlanPlantPot provided the answer for following solution on coronaLabs:
I would use the enterFrame function instead. Your timer won't go up in single milliseconds (it will increase by however many ms have passed in each frame), but nobody would be able to read that fast anyway.
local prevFrameTime, currentFrameTime --both nil
local deltaFrameTime = 0
local totalTime = 0
local txt_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 )
txt_counter.x = 150
txt_counter.y = 288
txt_counter:setTextColor( 255, 255, 255 )
group:insert( txt_counter )
and
local function enterFrame(e)
local currentFrameTime = system.getTimer()
--if this is still nil, then it is the first frame
--so no need to perform calculation
if prevFrameTime then
--calculate how many milliseconds since last frame
deltaFrameTime = currentFrameTime - prevFrameTime
end
prevFrameTime = currentFrameTime
--this is the total time in milliseconds
totalTime = totalTime + deltaFrameTime
--multiply by 0.001 to get time in seconds
txt_counter.text = totalTime * 0.001
end
I'm pretty new and have looked up my question to no avail, What I have are objects like balloons that float from bottom of the screen to the top, my problem is the spawnBallons function is only called once so only one object appears I want to call the function multiple times to spawn multiple objects and increase the number of objects spawned every 20 seconds?
function spwanBalloons()
local allBalloons = {"green_balloon2.png", "red_balloon.png"}
ballons = display.newImage(allBalloons[math.random(#allBalloons)])
ballons.x = math.random(display.contentWidth)
ballons.y = display.contentHeight + 60
transition.to( ballons, { time=math.random(3500-speedBump, 4500-speedBump), y=-100} )
speedBump = speedBump + 15
end
function startGame()
scoreText = display.newText( "Score: 0", 0, 0, "Helvetica", 22 )
scoreText.x = centerX
scoreText.y = display.screenOriginY + 10
spwanBalloons()
end
I tried this timer.performWithDelay( 500, spwanBalloons, 50 ) But All it does is spawn 50 objects over a half a second, I want to spawn a random amount of object until I tell it to stop?
This should spawn 1 balloon every half second for 20 seconds, then 2 balloons every half second, etc.
You can stop the timers with stopSpawnIncrease() and stopSpawn() as needed.
local spawnIncreaseTimer
local spawnNumber=0
local function spawnIncrease()
spawnNumber=spawnNumber+1
spawnIncreaseTimer = timer.performWithDelay( 20000, spawnIncrease)
end
function stopSpawnIncrease()
timer.cancel( spawnIncreaseTimer )
end
local spawnTimer
function spwanBalloons()
for i=1,spawnNumber do
local allBalloons = {"green_balloon2.png", "red_balloon.png"}
ballons = display.newImage(allBalloons[math.random(#allBalloons)])
ballons.x = math.random(display.contentWidth)
ballons.y = display.contentHeight + 60
transition.to( ballons, { time=math.random(3500-speedBump, 4500-speedBump), y=-100} )
speedBump = speedBump + 15
end
spawnTimer = timer.performWithDelay( 500, spwanBalloons )
end
function stopSpawnTimer()
timer.cancel( spawnTimer )
end
function startGame()
scoreText = display.newText( "Score: 0", 0, 0, "Helvetica", 22 )
scoreText.x = centerX
scoreText.y = display.screenOriginY + 10
spawnIncrease() -- first call brings from 0 to 1 and starts timer
spwanBalloons()
end
I have this line in one of my scenes:
[self schedule:#selector(storeValue:) interval:1.0/30.0];
storeValue is very basic and fast. It just stores the position of a layer on an NSMutableArray. I need this storeValue to be called in as much precise as possible timings, but after making some measurements, these are the intervals measured between each storeValue call:
interval 0 = 0
interval 1 = 0.049962
interval 2 = 0.033345
interval 3 = 0.033332
interval 4 = 0.049994
interval 5 = 0.050050
interval 6 = 0.049968
interval 7 = 0.033998
interval 8 = 0.049331
interval 9 = 0.050015
interval 10 = 0.049979
interval 11 = 0.049999
interval 12 = 0.033357
interval 13 = 0.033307
interval 14 = 0.049997
interval 15 = 0.033322
interval 16 = 0.050317
interval 17 = 0.049743
interval 18 = 0.049973
interval 19 = 0.033322
interval 20 = 0.050024
interval 21 = 0.049975
interval 22 = 0.049987
interval 23 = 0.033316
interval 24 = 0.050038
interval 25 = 0.050149
interval 26 = 0.049852
interval 27 = 0.049989
interval 28 = 0.050011
So, as you see, the method is called with a variety of intervals instead of always being 0.03333 (1 / 30).
I have tried to remove all code from storeValue to see how frequently storeValue is called and obtained the same irregular timings.
The big question is this: what should I do to improve schedule precision? Should I use NSTimer? GCD any other method of doing a scheduled task? Any suggestions?
thanks.
NOTE: I've discovered now that if I put interval = 0, storeValue will be called every frame, that means 1/60s and the precision is awesome. OK, I can make a logic to call storeValue half of the time but it would be nice to know why schedule is so imprecise and if there is a way to improve it.
When you schedule something, the selector and timer frequency are passed to Cocos2d sharedScheduler. Then sharedDirector calls [[Scheduler sharedScheduler] tick: dt]; on each frame. If your timer passes its threshold the scheduler calls your selector.
So the frequency cannot be more precise than mainloop frequency (max 1/60, min - depending on actual scene complexity). Probably 1/30 passes threshold not exactly on each 2nd frame and thats why you have not precise values in log.
I can propose to call your selector on other thread via NSTimer without using Cocos2d scheduler.
iOS isn't a Real-time operating system and thus you can never be guaranteed that your scheduled callback/NSTimer/etc. will ever occur at precisely 1/30 of a second. This precision will always vary based on what else the OS is handling.
What you can do, however, is use the actual time passed between each callback to account for this lack of consistency.