How to add a Loading bar

I need to load a number of assets and it takes more than a few seconds so I want to put up a loading bar showing progress. This would be a graphical image (not just a rectangle).

The problem is that I cannot get the screen to update in between loading each chunk of data. I have tried a number of approaches, but none seem to work. Looking at other similar queries on the subject has not yielded any solutions to this issue.

Typically you would handle this with a callback to update the screen, but Corona does not appear to have any explicit function to “update” the screen. What I thought would work within the structure of Corona’s architecture was using the timer.performWithDelay() function, but this does not work. Here is a sample of what I tried:

[lua]-- Sample Load screen.

– This *should* show a meter bar advancing as each chunk is loaded.
– Unfortunately it doesn’t seem to yield to the Corona SDK to let it update the display.

module(…, package.seeall)

local bUseListener = true
local nLoadIndex
local nLoadCount
local nMeterX = 161 – final X position of the meter bar when at 100 percent
local nMeterY = 508 – Y position of the meter bar
local nMeterW = 480 – Width of my meter bar image
local nMeterH = 30 – Height of my meter bar image

– These scale factors are because my source content is 800x600
– By using these I can change display modes (devices) and everything scales correctly
local nScaleX = display.contentWidth/800
local nScaleY = display.contentHeight/600

– Initialize the load variables
function LoadInit()
nLoadIndex = 1
nLoadCount = 20 – This is the number of “chunks” of data
end

– Load the next “chunk” of data. Each chunk is an sprite sheet
function LoadChunk()
– This is where I load my data. I have removed the calls to
– sprite.newSpriteSheetFromData since they aren’t necessary
– to show the display issue

– Now get ready for the next chunk, if any
nLoadIndex = nLoadIndex + 1
end

– Handle any finalization of the data here
function LoadComplete()
end

– Main function - MUST return a display.newGroup()
function new()
local splashGroup = display.newGroup()
local loadingImage, loadingBar, loadingMask

loadingImage = display.newImageRect( “load_bkgd.png”, display.contentWidth, display.contentHeight )
splashGroup:insert( loadingImage )
loadingImage.x = display.contentCenterX
loadingImage.y = display.contentCenterY

loadingBar = display.newImageRect( “load.png”, math.floor( nMeterW * nScaleX ), math.floor( nMeterH * nScaleY ) )
splashGroup:insert( loadingBar )

loadingBar:setReferencePoint( display.TopLeftReferencePoint )
loadingBar.x = math.floor( (nMeterX-nMeterW) * nScaleX )
loadingBar.y = math.floor( nMeterY * nScaleY )

loadingMask = display.newImageRect( “load_mask.png”, math.floor( 800 * nScaleX + 0.5 ), math.floor( 99 * gWS.nScaleY + 0.5 ) )
splashGroup:insert( loadingMask )

loadingMask:setReferencePoint( display.TopLeftReferencePoint )
loadingMask.x = 0
loadingMask.y = math.floor( (600-99)* nScaleY + 0.5)

– This was originally called via a Runtime event for “enterFrame”. It makes no
– difference if you change the code to do it that way or call it explicitly, you
– still do not see the bar advance until all of the disk I/O is done.
function updateBar()
local nPercent = math.floor( 100 * nLoadIndex / nLoadCount )
print( "Percent = ", nPercent )
loadingBar.x = loadingBar.x + math.floor( (nMeterX-nMeterW + (nMeterW*nPercent/100)) * nScaleX )
if ( nPercent >= 100 ) then
loadingBar.x = math.floor( nMeterX * nScaleX )
end
end

function myLoadChunk()
– Load a chunk of data
LoadChunk()
if ( bUseListener == false ) then
– update the loading bar. See the comments above
– regarding using a event to trigger this
updateBar()
end

– Are there any chunks remaining? If so, set a timer to
– load the next one. This *should* be non-blocking, but
– if you actually load data from a file in LoadChunk() this
– never yields to the SDK so it can update the screen.
if ( nLoadIndex <= nLoadCount ) then
timer.performWithDelay( 100, myLoadChunk )
else
if ( bUseListener == true ) then
Runtime:removeEventListener( “enterFrame”, updateBar )
end
– Done, so finish any data stuff
LoadComplete()
– Onward and outward
director:changeScene( “cSceneGame” )
end
end

– Set everything up so we can start going
LoadInit()

– Start it going
myLoadChunk()

if ( bUseListener == true ) then
Runtime:addEventListener( “enterFrame”, updateBar )
end

clean = function()

if loadingImage then
display.remove( loadingImage )
loadingImage = nil
end

if loadingBar then
display.remove( loadingBar )
loadingBar = nil
end

if loadingMask then
display.remove( loadingMask )
loadingMask = nil
end
end

– MUST return a display.newGroup()
return splashGroup
end[/lua] [import]uid: 16734 topic_id: 16253 reply_id: 316253[/import]

Ken,
You are in a similar boat as many other that want a breather, something like a DoEvents()

you might also want to read on this

The closest solution that I can suggest to you for this is using coroutines and *maybe* a timer or using *enterFrame*

cheers,

?:slight_smile: [import]uid: 3826 topic_id: 16253 reply_id: 60519[/import]

@JayantV,

I took a look at your blog post and although coroutines are very interesting, you mention at the end of the article that you couldn’t use it to display the values during the loop.

If you check out the sample I posted I used both timers and the “enterFrame” listener, but they didn’t work at all.

Is there anyone at Ansca that reads these posts? This is in the Paid Developer section specifically with the hope that they will read it and respond. I’m hoping there is a solution, but even a response saying “no, you cannot do that” is better than no response at all.

Are there ANY shipped titles that use Corona and have a Load Progress bar or an animation that is updated during loading? [import]uid: 16734 topic_id: 16253 reply_id: 60554[/import]

Ken,
I understand your frustrations, but just wondering aren’t you going to end up loading the spritesheets 20 times over? You will have to split the task into smaller manageable tasks, even in you have to run a long task of auto populating some arrays. so in the fist chunk you load the player sprites, in the second chunk, you load enemy1, in the next one enemy2, and so on…
Cheers,

?:slight_smile: [import]uid: 3826 topic_id: 16253 reply_id: 60573[/import]

@JayantV,

That is exactly what I am doing. Each “chunk” is a spritesheet. I have an array with the necessary parameters to create the spritesheet and each call to LoadChunk() grabs one element from the array, creates the spritesheet, etc.

It sounds like you think I am trying to load everything at once, which is the whole point. I specifically broke it up into chunks so there would be a “sleep” time in between chunks, thinking that would yield back to the Corona SDK so it could update the screen.

If you need a more complete sample to see what I am doing I can gladly provide it, but really I am doing exactly what you suggest. [import]uid: 16734 topic_id: 16253 reply_id: 60624[/import]

@Kem,
do not take it otherwise, there are times when the most basic of things are missed out, so just wanted to be sure that you were managing it correctly.

Have you tried to use co-routines?

You can send me the code and Let me see if I can help you fix that

cheers,

?:slight_smile: [import]uid: 3826 topic_id: 16253 reply_id: 60629[/import]

I’ve figured out the issue and will post a complete Example of a working Progress Loading bar in the Code Exchange section. The sample is being released under the MIT license so people can freely do what they want with it.

For those curious about the “solution” and who do not want to look at the Example, the problem was in line 71 above which resulted in the X position being off the screen the entire time it was updating. [import]uid: 16734 topic_id: 16253 reply_id: 60696[/import]

The complete example is in the Code Exchange at:

http://developer.anscamobile.com/code/load-progress-bar [import]uid: 16734 topic_id: 16253 reply_id: 60705[/import]

Was just about to reply to this and saw you got it working.

Well done :slight_smile: [import]uid: 84637 topic_id: 16253 reply_id: 60722[/import]

@KenRogoway, would you mind pointing me in the right direction to use your method to load sounds as well as images? [import]uid: 80305 topic_id: 16253 reply_id: 81460[/import]

@Insurgent Pixel,

Sure, it’s pretty straight forward.

In the cAssetLoader module you would add your files to load, along with a “file type” so your LoadChunk() can know whether or not it is a image, sprite sheet, sound, streaming sound, etc.

Assuming you are using the code I provided you’d do something like this:

[lua]---------------------------------------------------------------------------------------
– Date: January 21, 2012

– Version: 1.1

– File name: cAssetLoader.lua

– Code type: Example Code

– Author: Ken Rogoway

– Update History:

– Comments: The space images used are from NASA and are in the public domain.
– The horse image sheets are from the horse demo provided by Ansca.

– Sample code is MIT licensed:
– Permission is hereby granted, free of charge, to any person obtaining a copy
– of this software and associated documentation files (the “Software”), to deal
– in the Software without restriction, including without limitation the rights
– to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
– copies of the Software, and to permit persons to whom the Software is
– furnished to do so, subject to the following conditions:

– The above copyright notice and this permission notice shall be included in
– all copies or substantial portions of the Software.

– THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
– IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
– FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
– AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
– LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
– OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
– THE SOFTWARE.

– Copyright © 2011 Ken Rogoway. All Rights Reserved.

module(…, package.seeall)

require “sprite”

FILE_TYPE_IMAGE = 1
FILE_TYPE_IMAGE_RECT = 2
FILE_TYPE_SHEET = 3
FILE_TYPE_SOUND = 4
FILE_TYPE_STREAM = 5

– These are the require statements needed to load the LUA files
– associated with the sheets. For this example I am just using
– the uma “horse” sheet from the Ansca sample code found in:
– Sample Code\Sprites\HorseAnimation
sheetDataUma1 = require “uma1”
sheetDataUma2 = require “uma2”
sheetDataUma3 = require “uma3”
sheetDataUma4 = require “uma4”

– This is the info we use to define our chunks so we can
– load the data one chunk at a time. You can replace these
– sprite sheets with your own data, or add additional types
– of data to load as long as you handle that in LoadChunk()
DataChunk =
{
{ type=FILE_TYPE_IMAGE, sht=nil, file=“background.png”, w=0, h=0, ptr=nil },
{ type=FILE_TYPE_IMAGE_RECT, sht=nil, file=“logo.png”, w=100, h=100, ptr=nil },
{ type=FILE_TYPE_SHEET, sht=sheetDataUma1, file=“uma1.png”, w=0, h=0, ptr=nil },
{ type=FILE_TYPE_SHEET, sht=sheetDataUma2, file=“uma2.png”, w=0, h=0, ptr=nil },
{ type=FILE_TYPE_SHEET, sht=sheetDataUma3, file=“uma3.png”, w=0, h=0, ptr=nil },
{ type=FILE_TYPE_SHEET, sht=sheetDataUma4, file=“uma4.png”, w=0, h=0, ptr=nil },
{ type=FILE_TYPE_SOUND, sht=nil, file=“boing.wav”, w=0, h=0, ptr=nil },
}

– Set up our chunk start, count, etc so we can begin reading in data
function Initialize()

gWS.nLoadIndex = 1
gWS.nLoadCount = #DataChunk

end

– Yes, you could pass in the chunk index, but since this will be
– called by a timer, you cannot pass any parameters, hence the use
– of the nLoadIndex and nLoadCount global variables
function LoadChunk()

if ( gWS.nLoadIndex > 0 and gWS.nLoadIndex <= gWS.nLoadCount ) then
local chunk = DataChunk[gWS.nLoadIndex]
local fileName = chunk.file

if ( chunk.type == FILE_TYPE_IMAGE ) then
chunk.ptr = display.newImage( fileName )
elseif ( chunk.type == FILE_TYPE_IMAGE_RECT ) then
chunk.ptr = display.newImageRect( fileName, chunk.w, chunk.h )
elseif ( chunk.type == FILE_TYPE_SHEET ) then
local data = chunk.sht.getSpriteSheetData()

if ( data ) then
local spriteSheet = sprite.newSpriteSheetFromData( fileName, data )
if ( spriteSheet ) then
local nFrames = #data.frames
chunk.ptr = sprite.newSpriteSet( spriteSheet, 1, nFrames )
else
print( “*** ERROR *** Unable to newSpriteSheetFromData()” )
end
else
print( “*** ERROR *** Unable to getSpriteSheetData()” )
end
elseif ( chunk.type == FILE_TYPE_SOUND ) then
chunk.ptr = audio.loadSound( fileName )
elseif ( chunk.type == FILE_TYPE_STREAM ) then
chunk.ptr = audio.loadStream( fileName )
end
– Get ready for the next chunk
gWS.nLoadIndex = gWS.nLoadIndex + 1
end

end

– We are done with our loading
function Shutdown()

– Ensure that if LoadChunk() is called, we do nothing
gWS.nLoadIndex = #DataChunk + 1

end

– This is just hear so you can call it to load everything.
– Normally you would want to load a chunk at a time, and
– update the screen to reflect your progress.
– This is NOT the place to change or “fix” anything, this
– is just here so you can see how to load everything. Look
– in cSceneLoad.lua for the actual loading method being used.

function LoadAll()

Initialize()

while gWS.nLoadIndex <= gWS.nLoadCount do
LoadChunk()
end

Shutdown()

end[/lua] [import]uid: 16734 topic_id: 16253 reply_id: 81513[/import]

@KenRogoway, thanks a lot for this! [import]uid: 80305 topic_id: 16253 reply_id: 81620[/import]

This is WAAAAY beyond my ability at the moment. Need something like this but using Storyboard and not Director class.

Any examples of this anyone knows about ? [import]uid: 134089 topic_id: 16253 reply_id: 106381[/import]