Picture Book/ Ebook a lot of pages == a lot of .lua files ?

Hi all,

I just finished an ebook demo and got some questions.
Please take a look this screen record:
https://youtu.be/T-dhP8G2nW0

The entire picture book are 30 pages. I connected 6 pages so far and I have a concern. Now each page have 3 buttons, Left goes to the previous page, Right goes to the right page, and one Replay button to replay the story voice. The Do, Re, Mi sounds are just placeholders. I did all 3 buttons on page2.lua (as you can see the light purple page number at the bottom of each page). Then I duplicated it to be as page3.lua, page4.lua, page5.lua, … etc, and then go inside each lua to change background image, buttons, and page numbers… etc. Here are my concern. What if I finished all 30 pages and then I need to change the format/font of text, or add/moving a button? I wonder if any way I can make something like “main template” to place the buttons/ page text/ setting fonts (kinda like a concept of main layout?).

So far I only can think of that, the things aren’t change often can be placed in main.lua, or not destroyed by Composer. However, I have just learned lua and Corona SDK 1 month. Would you give me an idea if this could be implemented or quite impossible? Or an idea that how I could implement it?

 I would also like to know if I cannot find any soultions and, will the build crash if I do include 30 .lua files? :lol:

In case you need to check my very newbie code, you can download it here.

(I changed asset file name into an order, and include a few assets for asking question purpose. )

I modified it from Corona Simulator>New Project > eBook template.

Thanks for your help in advance!

Olina

You could have a thousand lua files and it wont be a problem,  but making one scene file per page for a storybook is the hard way to do this.

I’m really pressed for time right now, but I’ll re-read this and answer again later, OK?

Make a file called pages.lua with contents similar to:

local pages = {} pages[1] = { imageName = "imageforpage1.png", width = 150, height = 200, soundFile = "page1.wav" } pages[2] = { imageName = "imageforpage2.png", width = 150, height = 200, soundFile = "page2.wav" }pages[3] = { imageName = "imageforpage3.png", width = 150, height = 200, soundFile = "page3.wav" } return pages

Of course put in real values for what you’re using. You might want other parameters like the x and y to draw the image. If you have multiple images or audio files you can add them too. Have one record per page in your app.

Then create a single composer based Lua file that you will go to from your title page.  I’m not going to create the whole thing for you, but I think my concept we were talking about over email will best be understood with code: Something like:

local composer = require( "composer" ) local scene = composer.newScene() -- ----------------------------------------------------------------------------------- -- Code outside of the scene event functions below will only be executed ONCE unless -- the scene is removed entirely (not recycled) via "composer.removeScene()" -- ----------------------------------------------------------------------------------- local pages = require("pages") -- load the pages module of page information local currentPage = 1 -- first time here, initialize to page 1. You might want to track this in another data module or composer variable. local maxPages = #pages local mainImage local currentAudio -- this function will render the things that change on each page removing the old first, then adding the new second local function changePage( pageNumber ) local sceneGroup = scene.view -- put code here to remove your art and audio if mainImage and mainImage.removeSelf then display.remove(mainImage) end -- put code here to create the art for this page such as: mainImage = display.newImageRect( pages[pageNumber].imageName, pages[pageNumber].width, pages[pageNumber].height) sceneGroup:insert( mainImage ) end -- create your button handling functions here. You will want one for both the left and right buttons and the replay button -- for the left and right button you either increment or decrement currentPage making sure that if you exceed the max pages -- or subtract to 0 or less you handle that condition. In this case I dont change pages. Other options is to wrap around -- Your replay button would just play your audio again, no need to reload it, just replay it. local function handleRightButton( event ) currentPage = currentPage + 1 if currentPage \> maxPages then currentPage = maxPages end changePage( currentPage ) end -- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() function scene:create( event ) local sceneGroup = self.view -- Code here runs when the scene is first created but has not yet appeared on screen -- create your buttons here and any other thing thats the same from page to page. -- then call the function draw the first page. changePage( currentPage ) end -- not sure what else you will need to do in these functions, but for your basic app, probably not much. -- show() function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is still off screen (but is about to come on screen) elseif ( phase == "did" ) then -- Code here runs when the scene is entirely on screen end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view -- Code here runs prior to the removal of scene's view end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene

I know that last block is pretty long, it’s the standard Composer template so I left a lot of code in you may not need as placeholders.

Rob

Hi roaminggamer and Rob,

Thank you very much for all quick replies.

Hi Rob, it makes sense to me and I will try it out tonight. I learn a good concept from this case.
For now, I will mark this thread as solved to reduce the question amount from forum.

Thank you all very very much!

Olina

Hi Rob,

I am excited to show you the result, and I still have one major question though.

Please take a look this screen record:
https://youtu.be/lE3pa6uj02c

Structure explanation:

  1. I still keep the title.lua of eBook template as my cover page, and then jump to page_main.lua, my ebook main template.
  2. For debug purpose, I only set 10 pages of book, include the cover page. The data of page 2 to page10 are called.

Question:

  1. As the video 0:37 shown, after I click the next button of page10, it’s goes back to title.lua, this is great. However, page 2 is shown during the transition. I understand this is caused by I set currentPage = 2 at the end of handleRightButton function. I tried other numbers, for example currentPage = 10 (the final page), or currentPage = maxPages, but the second round of reading got page 10 as the following page of the title page though. How to avoid it from showing?

  2. As for pageNumber value of changePage(), I am very surprised with pageNumber knows automatically itself should be 1~ 10, an integer. I don’t understand why here everything works fine, without a for loop to iterate a number from 1 to 10 and been assigned to pageNumber?

In case you need to check my code, you can download it Here. (picBook_Olina-Copy_0320.zip)

There are actually more questions related to scene.view and .removeSelf, but I know I should ask after studying more.

Again, thank you all very much in advance!

Olina

Have you thought about having your content as html and loading it dynamically?  You can still keep your UI in Corona but just load page1.html, page2.html, etc.

local function handleRightButton( event ) currentPage = currentPage +1 if currentPage \> maxPages then composer.gotoScene( "title", "slideLeft", 800 ) currentPage = 2 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return --\<------ notice I added this end changePage( currentPage ) print("currentPage is "..currentPage) end local function handleLeftButton( event ) currentPage = currentPage -1 if currentPage \< 2 then composer.gotoScene("title", "fade", 500) currentPage = 2 --\<------- this probably should be page 10 or #pages &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return --\<------ notice I added this end changePage( currentPage ) print("currentPage is "..currentPage) end

Lets look at these two functions. In your flow, when you call composer.gotoScene, the code continues to execute and even though you went to the title scene, “changePage( currentPage )” still executes. By inserting a return where I did, you’re basically saying don’t bother executing the rest of the code. You could have accomplished the same thing using an if-then-else statement:

local function handleLeftButton( event ) currentPage = currentPage -1 if currentPage \< 2 then composer.gotoScene("title", "fade", 500) currentPage = 2 else changePage( currentPage ) print("currentPage is "..currentPage) end end

Now page 2 vs. page 1. What is page 1? If it’s the same as the title page, then 2 for the one function and #pages for the other function would be the right values. If page 1 is a real page, then start on page 1 and wrap around to page 1. 

There are a few things you need to consider before moving too far into this.

  1. Should your flow be:  Title->Page 1->Page 2->…->Last Page->Credits and not wrap around (Credits page could have a button to go back to the title page, but you wouldn’t move backwards from the title page to the credits. or

  2. Should your app show the title page once and allow the user free ability to cycle either forward or backwards through the app, never returning to the title page? Your page scenes could contain buttons to go to the title, or to a help, credits, etc. scenes.

There are many ways to approach this, but it changes how you manage the wrapping code.

Rob

Hi adrianm,
 

Thanks for your suggestion. I noticed this idea in the books I bought (in Mandarin) and I haven’t yet had a chance to try it.

Some goals for my eBook project, although I don’t think if it would win me any money yet.

  1. I would like to put some interactive animation in a certain page. Since I was a 2D/3D game animator, I would like to show off animations for kids to tap on.

  2. I might try animation of Animate CC(Flash) to HTML 5 and embedded in Corona in the short future. I will be really curious about the result.

  3. I am not good at making animation with HTML/CSS/JS yet. I could have tap-and-play swf file which run on my portfolio website. Hum… I will try to embedded it too, maybe. If anyone have tried, please share your experience.

  4. I want my animation/sound to be packaged up, like the old time Micromedia Director to exe file. :rolleyes:

(Not sure if installing a build made by Corona SDK will execute this goal, but … arh… I am having fun to make my app. )

  1. I guess I am possessed by learning coding recently. I tried Android Studio last year, but it was too hard and my tuition was gone with wind. :frowning:

Olina

Hi Rob,

Thanks a lot!

I studied Tutorial: Tap/Touch Anatomy , so I tried “return true”, “return page”, “return XXX” … . Since " return" is a very general keyword, I had a hard time to find out what it really means and how to use it. However, I think I got it after your explanation.

Thanks for the reminder of the book structure flow. After thinking about it, I will go Title->Page 2->Page 3-> … ->Page# ->Title for now. Then, I found out what my coding issue really is! Now my lua file flow is:

main.lau -> title.lua -> page_main.lua (runs Page 2~Page#) -> title.lua -> page_main.lua … and warp around.

I thought when the second time page_main.lua was started, every variable should be reset, which means “local currentPage = 2” still working at the very beginning of page_main.lua. However, it’s not. Overall, while I was writing here, I figured out that I need a composer.removeScene() to remove the page_main.lua. So that everything can be reset after the title page is reached. After I try and error for hours, for where I should put it, eventually I got it!!!

Here is the final result!! So exciting!
https://youtu.be/K91c-tIZOTo

Thank you, Rob!
Cheers!!

Olina

Function calls are kind of like a stack of plates.  Your main program is the first plate and then you call your function and it’s like stacking a plate on top of the first one. To get rid of that second plate to get back the first plate, your function has to “return”. 

Languages like Lua have an implicit “return” statement when it comes to the functions “end” statement. There may be times where you want to exit your function, pop the plate off the stack before you get to the “end” statement.

As for how to call return, many languages require to return something. You will see a “return 0” in some cases or return false. Some languages assume if you don’t provide a value to return, it will return “nothing”. In Lua’s case nothing is “nil”. Lua also supports returning multiple values:

return true, 10, "hello"

would return all three values to the calling program.

When you call a function you can either do:

doSomething(someParameters)

In this case, you don’t care about what the function returns. It still returns values, you just choose to through them away. But if you prefix it with a variable assignment:

someVariable = doSomething(someParameters)

and your function returns some values, then you can pass data back from the function into the calling function.  If (with Lua) you return multiple values:

someVariable, someOtherVariable, anotherVariable = doSomething( someParameters )

then all three variables will be populated. Following the example further up the page, they would contain  true, 10, and “hello”.

Rob

I’ve used Corona to shell mobile websites and put those out on the app stores.  It works really well as long as your web pages are all designed using % rather than px.  If you’re using responsive web design you should have no problems.  

CSS transitions and HTML 5 animations have really come a long way recently.  You can use an HTML 5 canvas object for drawing anything you like really.

You could have a thousand lua files and it wont be a problem,  but making one scene file per page for a storybook is the hard way to do this.

I’m really pressed for time right now, but I’ll re-read this and answer again later, OK?

Make a file called pages.lua with contents similar to:

local pages = {} pages[1] = { imageName = "imageforpage1.png", width = 150, height = 200, soundFile = "page1.wav" } pages[2] = { imageName = "imageforpage2.png", width = 150, height = 200, soundFile = "page2.wav" }pages[3] = { imageName = "imageforpage3.png", width = 150, height = 200, soundFile = "page3.wav" } return pages

Of course put in real values for what you’re using. You might want other parameters like the x and y to draw the image. If you have multiple images or audio files you can add them too. Have one record per page in your app.

Then create a single composer based Lua file that you will go to from your title page.  I’m not going to create the whole thing for you, but I think my concept we were talking about over email will best be understood with code: Something like:

local composer = require( "composer" ) local scene = composer.newScene() -- ----------------------------------------------------------------------------------- -- Code outside of the scene event functions below will only be executed ONCE unless -- the scene is removed entirely (not recycled) via "composer.removeScene()" -- ----------------------------------------------------------------------------------- local pages = require("pages") -- load the pages module of page information local currentPage = 1 -- first time here, initialize to page 1. You might want to track this in another data module or composer variable. local maxPages = #pages local mainImage local currentAudio -- this function will render the things that change on each page removing the old first, then adding the new second local function changePage( pageNumber ) local sceneGroup = scene.view -- put code here to remove your art and audio if mainImage and mainImage.removeSelf then display.remove(mainImage) end -- put code here to create the art for this page such as: mainImage = display.newImageRect( pages[pageNumber].imageName, pages[pageNumber].width, pages[pageNumber].height) sceneGroup:insert( mainImage ) end -- create your button handling functions here. You will want one for both the left and right buttons and the replay button -- for the left and right button you either increment or decrement currentPage making sure that if you exceed the max pages -- or subtract to 0 or less you handle that condition. In this case I dont change pages. Other options is to wrap around -- Your replay button would just play your audio again, no need to reload it, just replay it. local function handleRightButton( event ) currentPage = currentPage + 1 if currentPage \> maxPages then currentPage = maxPages end changePage( currentPage ) end -- ----------------------------------------------------------------------------------- -- Scene event functions -- ----------------------------------------------------------------------------------- -- create() function scene:create( event ) local sceneGroup = self.view -- Code here runs when the scene is first created but has not yet appeared on screen -- create your buttons here and any other thing thats the same from page to page. -- then call the function draw the first page. changePage( currentPage ) end -- not sure what else you will need to do in these functions, but for your basic app, probably not much. -- show() function scene:show( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is still off screen (but is about to come on screen) elseif ( phase == "did" ) then -- Code here runs when the scene is entirely on screen end end -- hide() function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then -- Code here runs when the scene is on screen (but is about to go off screen) elseif ( phase == "did" ) then -- Code here runs immediately after the scene goes entirely off screen end end -- destroy() function scene:destroy( event ) local sceneGroup = self.view -- Code here runs prior to the removal of scene's view end -- ----------------------------------------------------------------------------------- -- Scene event function listeners -- ----------------------------------------------------------------------------------- scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) -- ----------------------------------------------------------------------------------- return scene

I know that last block is pretty long, it’s the standard Composer template so I left a lot of code in you may not need as placeholders.

Rob

Hi roaminggamer and Rob,

Thank you very much for all quick replies.

Hi Rob, it makes sense to me and I will try it out tonight. I learn a good concept from this case.
For now, I will mark this thread as solved to reduce the question amount from forum.

Thank you all very very much!

Olina

Hi Rob,

I am excited to show you the result, and I still have one major question though.

Please take a look this screen record:
https://youtu.be/lE3pa6uj02c

Structure explanation:

  1. I still keep the title.lua of eBook template as my cover page, and then jump to page_main.lua, my ebook main template.
  2. For debug purpose, I only set 10 pages of book, include the cover page. The data of page 2 to page10 are called.

Question:

  1. As the video 0:37 shown, after I click the next button of page10, it’s goes back to title.lua, this is great. However, page 2 is shown during the transition. I understand this is caused by I set currentPage = 2 at the end of handleRightButton function. I tried other numbers, for example currentPage = 10 (the final page), or currentPage = maxPages, but the second round of reading got page 10 as the following page of the title page though. How to avoid it from showing?

  2. As for pageNumber value of changePage(), I am very surprised with pageNumber knows automatically itself should be 1~ 10, an integer. I don’t understand why here everything works fine, without a for loop to iterate a number from 1 to 10 and been assigned to pageNumber?

In case you need to check my code, you can download it Here. (picBook_Olina-Copy_0320.zip)

There are actually more questions related to scene.view and .removeSelf, but I know I should ask after studying more.

Again, thank you all very much in advance!

Olina

Have you thought about having your content as html and loading it dynamically?  You can still keep your UI in Corona but just load page1.html, page2.html, etc.

local function handleRightButton( event ) currentPage = currentPage +1 if currentPage \> maxPages then composer.gotoScene( "title", "slideLeft", 800 ) currentPage = 2 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return --\<------ notice I added this end changePage( currentPage ) print("currentPage is "..currentPage) end local function handleLeftButton( event ) currentPage = currentPage -1 if currentPage \< 2 then composer.gotoScene("title", "fade", 500) currentPage = 2 --\<------- this probably should be page 10 or #pages &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return --\<------ notice I added this end changePage( currentPage ) print("currentPage is "..currentPage) end

Lets look at these two functions. In your flow, when you call composer.gotoScene, the code continues to execute and even though you went to the title scene, “changePage( currentPage )” still executes. By inserting a return where I did, you’re basically saying don’t bother executing the rest of the code. You could have accomplished the same thing using an if-then-else statement:

local function handleLeftButton( event ) currentPage = currentPage -1 if currentPage \< 2 then composer.gotoScene("title", "fade", 500) currentPage = 2 else changePage( currentPage ) print("currentPage is "..currentPage) end end

Now page 2 vs. page 1. What is page 1? If it’s the same as the title page, then 2 for the one function and #pages for the other function would be the right values. If page 1 is a real page, then start on page 1 and wrap around to page 1. 

There are a few things you need to consider before moving too far into this.

  1. Should your flow be:  Title->Page 1->Page 2->…->Last Page->Credits and not wrap around (Credits page could have a button to go back to the title page, but you wouldn’t move backwards from the title page to the credits. or

  2. Should your app show the title page once and allow the user free ability to cycle either forward or backwards through the app, never returning to the title page? Your page scenes could contain buttons to go to the title, or to a help, credits, etc. scenes.

There are many ways to approach this, but it changes how you manage the wrapping code.

Rob

Hi adrianm,
 

Thanks for your suggestion. I noticed this idea in the books I bought (in Mandarin) and I haven’t yet had a chance to try it.

Some goals for my eBook project, although I don’t think if it would win me any money yet.

  1. I would like to put some interactive animation in a certain page. Since I was a 2D/3D game animator, I would like to show off animations for kids to tap on.

  2. I might try animation of Animate CC(Flash) to HTML 5 and embedded in Corona in the short future. I will be really curious about the result.

  3. I am not good at making animation with HTML/CSS/JS yet. I could have tap-and-play swf file which run on my portfolio website. Hum… I will try to embedded it too, maybe. If anyone have tried, please share your experience.

  4. I want my animation/sound to be packaged up, like the old time Micromedia Director to exe file. :rolleyes:

(Not sure if installing a build made by Corona SDK will execute this goal, but … arh… I am having fun to make my app. )

  1. I guess I am possessed by learning coding recently. I tried Android Studio last year, but it was too hard and my tuition was gone with wind. :frowning:

Olina

Hi Rob,

Thanks a lot!

I studied Tutorial: Tap/Touch Anatomy , so I tried “return true”, “return page”, “return XXX” … . Since " return" is a very general keyword, I had a hard time to find out what it really means and how to use it. However, I think I got it after your explanation.

Thanks for the reminder of the book structure flow. After thinking about it, I will go Title->Page 2->Page 3-> … ->Page# ->Title for now. Then, I found out what my coding issue really is! Now my lua file flow is:

main.lau -> title.lua -> page_main.lua (runs Page 2~Page#) -> title.lua -> page_main.lua … and warp around.

I thought when the second time page_main.lua was started, every variable should be reset, which means “local currentPage = 2” still working at the very beginning of page_main.lua. However, it’s not. Overall, while I was writing here, I figured out that I need a composer.removeScene() to remove the page_main.lua. So that everything can be reset after the title page is reached. After I try and error for hours, for where I should put it, eventually I got it!!!

Here is the final result!! So exciting!
https://youtu.be/K91c-tIZOTo

Thank you, Rob!
Cheers!!

Olina

Function calls are kind of like a stack of plates.  Your main program is the first plate and then you call your function and it’s like stacking a plate on top of the first one. To get rid of that second plate to get back the first plate, your function has to “return”. 

Languages like Lua have an implicit “return” statement when it comes to the functions “end” statement. There may be times where you want to exit your function, pop the plate off the stack before you get to the “end” statement.

As for how to call return, many languages require to return something. You will see a “return 0” in some cases or return false. Some languages assume if you don’t provide a value to return, it will return “nothing”. In Lua’s case nothing is “nil”. Lua also supports returning multiple values:

return true, 10, "hello"

would return all three values to the calling program.

When you call a function you can either do:

doSomething(someParameters)

In this case, you don’t care about what the function returns. It still returns values, you just choose to through them away. But if you prefix it with a variable assignment:

someVariable = doSomething(someParameters)

and your function returns some values, then you can pass data back from the function into the calling function.  If (with Lua) you return multiple values:

someVariable, someOtherVariable, anotherVariable = doSomething( someParameters )

then all three variables will be populated. Following the example further up the page, they would contain  true, 10, and “hello”.

Rob