Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat
Show message if frozen for too long - Gideros Forum

Show message if frozen for too long

Hi everyone,

I've made a transition which animates in to fill the screen, performs any heavy tasks, then animates out. I use it for all scene transitions. For most scenes there is virtually no delay when switching screens, but for some it takes a second or so, because I initialise levels etc.

I would like to show a "please wait" message if the delay is longer than n seconds or frames. I guess the only way to do this is with Core.asyncCall? Since this is still a mystery to me, could someone shed light on how I could achieve the above?

Cheers,

Nic
My Gideros games: www.totebo.com

Comments

  • olegoleg Member
    edited July 6
    just add a message "please wait" before a longer task
    in the clean after the task

    Likes: Apollo14

    +1 -1 (+1 / -0 ) Share on Facebook
  • totebototebo Member
    Yeah, that's one option. But I don't always know if there will be a long wait, because it is determined by the speed of the device, and I don't want to show the message if the wait is very short.

    Isn't this the kind of thing Async is used for?
    My Gideros games: www.totebo.com
  • SinisterSoftSinisterSoft Maintainer
    Just put a check in for something you know should be quick, if it's slower then normal (but still not noticeable to the human eye) then set a flag 'slowDevice', if slow device is true then show the message on anything that normally takes a while.
  • totebototebo Member
    That's a nice fallback that could work, but I would much rather automate it. Is it impossible to use Async? I thought this was the sort of thing we added it for?
    My Gideros games: www.totebo.com
  • olegoleg Member
    @totebo
    --stream 1
    function move()
      for x=1,100 do
         sprite:setX(x)
    --перемикаємо на інший потік
     
         Core.yield(true)
      end
     
      for y=1,100 do
         sprite:setY(y)
    --перемикаємо на інший потік     Core.yield(true)
      end
     
      print ("Animation complete")
    end
     ----------------------------------------
     
    --stream 2
     
    function pulse()
      local i=0
     
      while (true) do
        sprite:setAlpha((math.sin(i))^2)
        i=i+0.1
    --перемикаємо на інший потік
     
        Core.yield(true)
      end
    end
     ----------------------------------------
     
    --основний цикл програми
     
    sprite=Shape.new()
    sprite:setFillStyle(Shape.SOLID,0xFF0000)
    sprite:moveTo(0,0)
    sprite:lineTo(100,0)
    sprite:lineTo(100,100)
    sprite:lineTo(0,100)
    sprite:endPath()
     
    stage:addChild(sprite)
     
    --запускаємо потоки
     
    Core.asyncCall(move)
    Core.asyncCall(pulse)

    Likes: totebo

    +1 -1 (+1 / -0 ) Share on Facebook
  • Apollo14Apollo14 Member
    I'm not sure if I understood correctly what you need. I would do that:
    function showPopup()
    	--gotta show popup if loading takes more than 1sec
    end
     
    popupTimer = Timer.new(1000,1)
    popupTimer:addEventListener(Event.TIMER, showPopup, popupTimer)
    popupTimer:start()
     
    function loadAssets()
    	for i=1,100000 do
    	--load lots of stuff
    	end
     
    	popupTimer:stop() --if everything is loaded before 1sec, popup will never be shown
    end
     
    Core.asyncCall(loadAssets)

    Likes: totebo

    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
    +1 -1 (+1 / -0 ) Share on Facebook
  • totebototebo Member
    edited July 7
    That looks great, will try it out. These solutions rely on Async to do the heavy task, right? Is there a way of doing the heavy task separately, and start using Async at the same time only to determine if it's taking too long, and if so show a message?
    My Gideros games: www.totebo.com
  • olegoleg Member
    edited July 7

    use os.time
    http://docs.giderosmobile.com/reference/lua/os/time
    for i=1,100000 do
     print("please wait",(os.time()-tm)*(100000-i),"sek")
    tm= os.time()
     
    --load lots of stuff
    end
  • totebototebo Member
    Do you mean using os.time inside the loop that is heavy, and then let the loop itself determine whether it's taking too long?
    My Gideros games: www.totebo.com
  • Apollo14Apollo14 Member
    Accepted Answer
    totebo said:

    That looks great, will try it out. These solutions rely on Async to do the heavy task, right? Is there a way of using Async to determine if it's taking too long, and if so show a message?

    There're 2 ways to use Core.asyncCall:
    http://giderosmobile.com/forum/discussion/6493/upcoming-background-tasks-support-in-gideros-a-quick-example
    Another usage:
    http://giderosmobile.com/forum/discussion/comment/48666/#Comment_48666)
    > Newcomers roadmap: from where to start learning Gideros
    "What one programmer can do in one month, two programmers can do in two months." - Fred Brooks
  • totebototebo Member
    Thanks!
    My Gideros games: www.totebo.com
  • olegoleg Member
    edited July 7 Accepted Answer
    --stream 1
    function move()
      for x=1,100 do
         sprite:setX(x)
    --перемикаємо на інший потік
     
         Core.yield(true)
      end
     
      for y=1,100 do
         sprite:setY(y)
    --перемикаємо на інший потік     Core.yield(true)
      end
     
      print ("Animation complete")
    end
     ----------------------------------------
     
    --stream 2
     
    function popup()
      local i=0
    max=100000 -- experimentally calculate
     
      while (true) do
        print("please wait",(os.time()-tm)*(max-i),"sek")
        tm= os.time()
    i=i+1
    print(i) -- experimentally calculate max=maximum i
    --перемикаємо на інший потік
     
        Core.yield(true)
      end
    end
     ----------------------------------------
     
    --основний цикл програми
     
    sprite=Shape.new()
    sprite:setFillStyle(Shape.SOLID,0xFF0000)
    sprite:moveTo(0,0)
    sprite:lineTo(100,0)
    sprite:lineTo(100,100)
    sprite:lineTo(0,100)
    sprite:endPath()
     
    stage:addChild(sprite)
     
    --запускаємо потоки
     
    Core.asyncCall(move)
    Core.asyncCall(popup)
  • totebototebo Member
    edited July 8
    First stumbling block. What am I missing here?
    Core.asyncCall( self.createGameworldLevel )
    Game.lua:1941: attempt to index local 'self' (a thread value)
    stack traceback:
    	Game.lua:1941: in function 'fn'
    	[string "local function _start_(fn,...) coroutine.yield() fn(...) end return _start_(...)"]:1: in function <[string "local function _start_(fn,...) coroutine.yield() fn(...) end return _start_(...)"]:1>
    	(tail call): ?


    Line 1941:
    self:resetExpeditionSeed()
    My Gideros games: www.totebo.com
  • totebototebo Member
    edited July 8
    Right, I went through and made all "self" a local variable to that specific class, like this:
    local resetExpeditionSeed
    Then, in the init function, I define it like this:
    resetExpeditionSeed = self.resetExpeditionSeed
    It works! I now have control of the loading process when the game used to be frozen. Is the way I solved it naughty? Will I end up on the naughty chair later down the line?

    Likes: antix, Apollo14

    My Gideros games: www.totebo.com
    +1 -1 (+2 / -0 ) Share on Facebook
  • antixantix Member
    If it works now it should work in the future :)

    You could always recode your level generation to be faster to :D
  • totebototebo Member
    Oh yeah for sure! The generation can probably be more efficient, but for the longer levels it will take a second or two, even on a fast device, because the levels are procedural and can be massive.
    My Gideros games: www.totebo.com
  • hgy29hgy29 Maintainer
    Core.asyncCall( self.createGameworldLevel, self )
    Would have been an easier fix...
    +1 -1 (+2 / -0 ) Share on Facebook
  • totebototebo Member
    Oh really, you can provide scope! Perfect. I suppose locals to self speeds things up anyway, so I don't feel too bad.

    Likes: antix

    My Gideros games: www.totebo.com
    +1 -1 (+1 / -0 ) Share on Facebook
  • SinisterSoftSinisterSoft Maintainer
    edited July 9
    I still think you should do something to check the time it takes on average to create the level dependent on time for that particular machine. Divide this by X*Y to get an average per map cell. Then just X * Y * AverageCellTime to find the time to make the map in advance. If above a certain time then unhide the loading sprite (always there, just hidden) before you even start the load/generation. At then end of loading, hide the loading sprite regardless - it doesn't matter if it was on or off.

    Likes: oleg

    +1 -1 (+1 / -0 ) Share on Facebook
  • totebototebo Member
    That would kind of work, but because each map "room" is completely different, they all take different time to load. I might add more rooms after release. If the game was more generic and more linearly structured it would make sense, but as it is this Async thing is the perfect solution!
    My Gideros games: www.totebo.com
  • totebototebo Member
    Question: Does Core.asyncCall stop automatically once the task has completed? Is there any cleanup needed?
    My Gideros games: www.totebo.com
  • hgy29hgy29 Maintainer
    Yes, the task is cleaned up automatically once the function finishes

    Likes: totebo, antix

    +1 -1 (+2 / -0 ) Share on Facebook
  • @hgy29 how to coroutine.create can wait for seconds like
    Core.yield([number of seconds to wait])

    Or is there any way to force Core.asyncCall runs in main thread like coroutine.create does?
    So that I can use Core.yield to handle the execute flow ...


    Coming soon
  • hgy29hgy29 Maintainer
    Core.asyncCall always execute in main thread, since gideros in single threaded, so just use asyncCall instead of coroutines. Fake threads are just coroutines managed by gideros
    +1 -1 (+1 / -0 ) Share on Facebook
  • edited July 10
    Oh Great! somehow when I read the docs:
    http://docs.giderosmobile.com/reference/gideros/Core/asyncCall#Core.asyncCall

    "Launch function on separate thread as background task.
    Background threads are only executed when main thread is not running"

    I used to think Core.asyncCall has low priority and only run in free time.

    Can you explain more about "when main thread is not running", what is calculate into the running of "main thread"? And when using Core.asyncCall, how can I ensure it has the high priority to complete the work?
    Thank so much!
    Coming soon
  • hgy29hgy29 Maintainer
    Yes, the docs are both right and wrong depending on which side you are considering. At lua side, main code, event handlers, and each function called through asyncCall can be considered as threads: as a programmer you don't need to schedule their executions.

    At Gideros level however, all lua code is run in the same thread, so there is no multicpu handling. First the main code is executed, then when all main code has finished running gideros enters the rendering loop, which does the following:
    1. Execute event handlers
    2. Draw the frame on backbuffer
    3. Execute of chunk of asyncCall'ed functions (which are lua coroutines)
    4. Swap backbuffer and frontbuffer and go back to 1

    in step 3, we compute the spare time we have before we need to go to 4 (based on fps), and execute has much of 'background' threads as we can, executing them at least once anyhow to ensure they don't stall.

    So those background threads have indeed low priority, and you'd not want them to have a higher priority then game rendering anyhow as it would make your game lag.
    +1 -1 (+2 / -0 ) Share on Facebook
  • Thank so much @hgy29 !
    Coming soon
Sign In or Register to comment.