In this post we will continue with the storyboard work we did in the previous post but we will be creating a more appealing look & feel by adding some art work and additional animations. Also we will add a level grid to the play game section.
Currently I’m in Indonesia, after clearing customs and security we both got very sick. We think we ate some bad chicken in Kuala Lumpur. Anyhow, we had severe food poisoning for a couple of days, resulting that we stayed for 4 days in southern Lombok. Getting food poisoning was part of our expectations, but being a first-timer I never thought it makes you feel that bad. We took 2 days ago a local boat to the Gili islands (northern Lombok). The boat was a really cool experience, we were sitting on a food supply boat for the islands, and beside another Dutch couple we were the only foreigners on the boat. Maneuvering with large backpacks between food and boxes with eggs was quite a challenge.
In this part of the Rolly Bear World project we will start adding images to our project. To keep a nice overview in your project outline create a folder called “images”. In this folder of the project we will add all the art work.
So let’s start with creating a background color. We will add this in our main.lua file so we don’t need to repeat the code in all screens throughout the game.
I added line 12 – 17 to main.lua. Line 12 creates a rectangle across the surface of the phone screen. We will fill this rectangle with the gradient we defined in line 13-16. All of your screens should now have a light blue background color.
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com -- easy to use variables for screen-positions centerX = display.contentWidth / 2 centerY = display.contentHeight / 2 withScrn = display.contentWidth heightScrn = display.contentHeight topScrn = display.screenOriginY leftScrn = display.screenOriginX backgroundfill = display.newRect(leftScrn, topScrn, withScrn, heightScrn) gradient = graphics.newGradient( { 80, 211, 255 }, {80, 100, 180 }, "up" ) backgroundfill:setFillColor(gradient) local storyboard = require ("storyboard") storyboard.purgeOnSceneChange = true storyboard.gotoScene ( "menu", { effect = "fade"} )
Next open your menu.lua. We will make quite some changes here:
1) Insert a Game Title
2) We create buttons instead of textfields
4) We add a floor to the the background, with a chest on top of it and a pip with a Rolly Bear animation.
5) When the user clicks on the chest, the chest will open and a heart (trophy) will appear.
As we will use the widget API from Corona SDK for creating some of the button functionalities, make sure you import the widget library in your menu.lua (see line 6).
I created a gametitle via Inkscape (use Github on the bottom of this post to download the file). This title is created in line 25-28. The 3 buttons: Play Game, Options and Credits, are replaced with wooden buttons. They are created in line 80 – 129. Most of this is self-explanatory, but the defaultFile and overFile are the different images when the user taps or release the buttons.
The floor is created in line 30-32, and the green pipe is created in line 73-76. For Rolly Bear I created a small animation, that is, he just peaks out of the pipe. To make this happen, we position Rolly Bear off the screen and use transition.to (Corona SDK Api Call) to move him upwards within 1.5 seconds (line 70).
For the animation of chest we need 3 images. One for the chest being closed, one for the chest being open, and one for the heart. In line 35-38 we postion the closed chest. In line 61 we add an eventlistener to this closed chest image. When the user taps the closed chest the openChest function will be triggered (line 42- 59). First we remove the closed chest image and just replace it with the open chest image (line 45-49). Next, we create the heart (trophy variable) and add an transition.to. In this transition we move the heart from within the chest up to the centerY and add a small xScale/yScale animation. When this transition is complete we call the function fadeAway (line 55-57).
Your menu.lua should look like this
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com local storyboard = require( "storyboard" ) local scene = storyboard.newScene() local widget = require("widget") -- local forward references should go here -- local function btnTap(event) storyboard.gotoScene ( event.target.destination, {effect = "fade"} ) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view -- CREATE display objects and add them to 'group' here. -- Example use-case: Restore 'group' from previously saved state. local title = display.newImage( "images/gameTitle.png" ) title.x = centerX title.y = topScrn + title.height group:insert(title) local floor = display.newImage ("images/floortitlescreen.png") floor.y = heightScrn group:insert(floor) local chestClosed = display.newImage("images/chestclosed.png") chestClosed.x = display.screenOriginX + 1.3 * chestClosed.width chestClosed.y = floor.y - floor.height group:insert(chestClosed) local function openChest() -- chest animation display.remove( chestClosed) local chestOpen = display.newImage("images/chestopen.png") chestOpen.x = display.screenOriginX + 1.3 * chestOpen.width chestOpen.y = floor.y - floor.height group:insert(chestOpen) -- heart / trophy animation local trophy = display.newImage ("images/heart64.png") trophy.x = display.screenOriginX + 1.3 * chestClosed.width trophy.y = floor.y - floor.height group:insert (trophy) local function fadeAway() transition.to( trophy, { time = 1500, xScale = 0, yScale =0 } ) end transition.to( trophy, { time = 1300, y = centerY, xScale = 1.2, yScale = 1.2, onComplete = fadeAway} ) end chestClosed:addEventListener("tap", openChest) local rollybear = display.newImage("images/rollybeartitlescreen.png") rollybear.x = withScrn - 144 rollybear.y = heightScrn * 1.5 rollybear.xScale = .45 rollybear.yScale = .45 transition.to( rollybear, {time= 1500, y = chestClosed.y -30 } ) group:insert(rollybear) local pipe = display.newImage("images/pipe.png") pipe.x = withScrn - pipe.width pipe.y = heightScrn - 30 group:insert(pipe) -- create custom buttom local playBtn = widget.newButton { width = 500, height = 90, defaultFile = "images/button_notpressed.png", overFile = "images/button_pressed.png", label="Play Game", labelColor = { default = { 250, 255, 250}, over ={0,0,0 }}, fontSize = "46", } playBtn.x = centerX playBtn.y = centerY*1.2 - 1.2*playBtn.height playBtn.destination = "levels" playBtn:addEventListener("tap", btnTap) group:insert(playBtn) local optionsBtn = widget.newButton { width = 500, height = 90, defaultFile = "images/button_notpressed.png", overFile = "images/button_pressed.png", label="Options", labelColor = { default = { 250, 255, 250}, over ={0,0,0 }}, fontSize = "46", } optionsBtn.x = centerX optionsBtn.y = centerY*1.2 optionsBtn.destination = "options" optionsBtn:addEventListener("tap", btnTap) group:insert(optionsBtn) local creditsBtn = widget.newButton { width = 500, height = 90, defaultFile = "images/button_notpressed.png", overFile = "images/button_pressed.png", label="Credits", labelColor = { default = { 250, 255, 250}, over ={0,0,0 }}, fontSize = "46", } creditsBtn.x = centerX creditsBtn.y = optionsBtn.y + 1.2 *creditsBtn.height creditsBtn.destination = "gamecredits" creditsBtn:addEventListener("tap", btnTap) group:insert(creditsBtn) end -- Called immediately after scene has moved onscreen: function scene:enterScene( event ) local group = self.view -- INSERT code here (e.g. start timers, load audio, start listeners, etc.) end -- Called when scene is about to move offscreen: function scene:exitScene( event ) local group = self.view -- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end -- Called prior to the removal of scene's "view" (display group) function scene:destroyScene( event ) local group = self.view -- INSERT code here (e.g. remove listeners, widgets, save state, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end --------------------------------------------------------------------------------- -- END OF YOUR IMPLEMENTATION --------------------------------------------------------------------------------- -- "createScene" event is dispatched if scene's view does not exist scene:addEventListener( "createScene", scene ) -- "enterScene" event is dispatched whenever scene transition has finished scene:addEventListener( "enterScene", scene ) -- "exitScene" event is dispatched before next scene's transition begins scene:addEventListener( "exitScene", scene ) -- "destroyScene" event is dispatched before view is unloaded, which can be -- automatically unloaded in low memory situations, or explicitly via a call to -- storyboard.purgeScene() or storyboard.removeScene(). scene:addEventListener( "destroyScene", scene ) --------------------------------------------------------------------------------- return scene
For the options and credits screen I made 2 minor changes. Both screens have now a title and back button based on an image. The adjusted lua files are below.
options.lua
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com local storyboard = require( "storyboard" ) local scene = storyboard.newScene() -- local forward references should go here -- local function btnTap(event) storyboard.gotoScene ( event.target.destination, {effect = "fade"} ) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view -- CREATE display objects and add them to 'group' here. -- Example use-case: Restore 'group' from previously saved state. local title= display.newImage ("images/optionstitle.png") title.x = centerX title.y = topScrn + title.height group:insert(title) local backbtn = display.newImage ("images/reloadbutton.png") backbtn.y = heightScrn - 0.6 * backbtn.height backbtn.x = .6 * backbtn.width backbtn.destination = "menu" backbtn:addEventListener("tap", btnTap) group:insert(backbtn) end -- Called immediately after scene has moved onscreen: function scene:enterScene( event ) local group = self.view -- INSERT code here (e.g. start timers, load audio, start listeners, etc.) end -- Called when scene is about to move offscreen: function scene:exitScene( event ) local group = self.view -- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end -- Called prior to the removal of scene's "view" (display group) function scene:destroyScene( event ) local group = self.view -- INSERT code here (e.g. remove listeners, widgets, save state, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end --------------------------------------------------------------------------------- -- END OF YOUR IMPLEMENTATION --------------------------------------------------------------------------------- -- "createScene" event is dispatched if scene's view does not exist scene:addEventListener( "createScene", scene ) -- "enterScene" event is dispatched whenever scene transition has finished scene:addEventListener( "enterScene", scene ) -- "exitScene" event is dispatched before next scene's transition begins scene:addEventListener( "exitScene", scene ) -- "destroyScene" event is dispatched before view is unloaded, which can be -- automatically unloaded in low memory situations, or explicitly via a call to -- storyboard.purgeScene() or storyboard.removeScene(). scene:addEventListener( "destroyScene", scene ) --------------------------------------------------------------------------------- return scene
credits.lua
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com local storyboard = require( "storyboard" ) local scene = storyboard.newScene() -- local forward references should go here -- local function btnTap(event) storyboard.gotoScene ( event.target.destination, {effect = "fade"} ) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view -- CREATE display objects and add them to 'group' here. -- Example use-case: Restore 'group' from previously saved state. local title= display.newImage ("images/creditstitle.png") title.x = centerX title.y = topScrn + title.height group:insert(title) local backbtn = display.newImage ("images/reloadbutton.png") backbtn.y = heightScrn - 0.6 * backbtn.height backbtn.x = .6 * backbtn.width backbtn.destination = "menu" backbtn:addEventListener("tap", btnTap) group:insert(backbtn) end -- Called immediately after scene has moved onscreen: function scene:enterScene( event ) local group = self.view -- INSERT code here (e.g. start timers, load audio, start listeners, etc.) end -- Called when scene is about to move offscreen: function scene:exitScene( event ) local group = self.view -- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end -- Called prior to the removal of scene's "view" (display group) function scene:destroyScene( event ) local group = self.view -- INSERT code here (e.g. remove listeners, widgets, save state, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end --------------------------------------------------------------------------------- -- END OF YOUR IMPLEMENTATION --------------------------------------------------------------------------------- -- "createScene" event is dispatched if scene's view does not exist scene:addEventListener( "createScene", scene ) -- "enterScene" event is dispatched whenever scene transition has finished scene:addEventListener( "enterScene", scene ) -- "exitScene" event is dispatched before next scene's transition begins scene:addEventListener( "exitScene", scene ) -- "destroyScene" event is dispatched before view is unloaded, which can be -- automatically unloaded in low memory situations, or explicitly via a call to -- storyboard.purgeScene() or storyboard.removeScene(). scene:addEventListener( "destroyScene", scene ) --------------------------------------------------------------------------------- return scene
In levels.lua I made some more changes. I created a level grid. Note that this is not yet working fully as it does not include yet any storage or interaction. In line 9 – 14 I created a table holding values to represent the state of the level. Eventually, this table will hold the saved values of the levelprogress of the user. Value 1 in the table means that the level is playable by the user, and value 2 means the user cannot play that level yet. A second table holds the images of each of the two values (or states).
In line 34-52 I create the actual level grid and use the tables above the determine if the level should be open or closed to the user, and what image to display (open level or locked). The table is created by a for-loop with 2 rows and 5 columns (so 10 levels in total). I created a variable levelIndex to look into the levels table. Initially set the levelIndex to zero, and increment with 1 in each loop. As such we also loop through the levels table. the levelImg picks then the right image from the images table. Finally we position the levelimg and put them 175 pixels apart (both X and Y).
In line 46-52, we create an if-statement that we list the level number in the icon when the level is not locked. This is a bit sloppy from my side as a user might one to be able to read all level numbers despite of being locked or not. I might make this and some other things more clean when we build the level grid completely with functionality.
The complete levels.lua
-- Rolly Bear World Project by Christian Peeters -- See all tutorial @christian.peeters.com local storyboard = require( "storyboard" ) local scene = storyboard.newScene() -- local forward references should go here -- levels = { 1, 2, 2, 2 , 2, --1 means level is open to be played 2, 2, 2, 2, 2, --2 means level is locked 2, 2, 2, 2, 2 } images ={ { getFile = "images/leveliconnotpressed.png", types = "play" }, { getFile = "images/levellocked.png", types = "locked"} } local function btnTap(event) storyboard.gotoScene ( event.target.destination, {effect = "fade"} ) return true end -- Called when the scene's view does not exist: function scene:createScene( event ) local group = self.view -- CREATE display objects and add them to 'group' here. -- Example use-case: Restore 'group' from previously saved state. local levelIndex =0 for i=0,1 do for j=1,5 do tablePlace = i*5 + j levelIndex = levelIndex + 1 local imagesId = levels[levelIndex] levelImg = display.newImageRect (images[imagesId].getFile , 150, 150 ) levelImg.x = 120 + (j* 175 ) levelImg.y = 300 + (i* 175 ) group:insert(levelImg) if images[imagesId].types == "play" then leveltxt = display.newText("Level "..tostring(tablePlace), 0,0, "Helvetica", 30) leveltxt.x = 120 + (j*175) leveltxt .y = 300+ (i*175) leveltxt:setTextColor (250, 255, 251) group:insert (leveltxt) end end end local title= display.newImage ("images/levelstitle.png") title.x = centerX title.y = topScrn + title.height group:insert(title) local backbtn = display.newImage ("images/reloadbutton.png") backbtn.y = heightScrn - 0.6 * backbtn.height backbtn.x = .6 * backbtn.width backbtn.destination = "menu" backbtn:addEventListener("tap", btnTap) group:insert(backbtn) end -- Called immediately after scene has moved onscreen: function scene:enterScene( event ) local group = self.view -- INSERT code here (e.g. start timers, load audio, start listeners, etc.) end -- Called when scene is about to move offscreen: function scene:exitScene( event ) local group = self.view -- INSERT code here (e.g. stop timers, remove listeners, unload sounds, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end -- Called prior to the removal of scene's "view" (display group) function scene:destroyScene( event ) local group = self.view -- INSERT code here (e.g. remove listeners, widgets, save state, etc.) -- Remove listeners attached to the Runtime, timers, transitions, audio tracks end --------------------------------------------------------------------------------- -- END OF YOUR IMPLEMENTATION --------------------------------------------------------------------------------- -- "createScene" event is dispatched if scene's view does not exist scene:addEventListener( "createScene", scene ) -- "enterScene" event is dispatched whenever scene transition has finished scene:addEventListener( "enterScene", scene ) -- "exitScene" event is dispatched before next scene's transition begins scene:addEventListener( "exitScene", scene ) -- "destroyScene" event is dispatched before view is unloaded, which can be -- automatically unloaded in low memory situations, or explicitly via a call to -- storyboard.purgeScene() or storyboard.removeScene(). scene:addEventListener( "destroyScene", scene ) --------------------------------------------------------------------------------- return scene
In case of any questions or comments please leave them below. I hope you’re still enjoying the posts.
Why is my background like this( http://imgur.com/woXNsLr )? All my code are exactly like yours :(
Hi Alexander,
Did you define in you main.lua the background?
backgroundfill = display.newRect(leftScrn, topScrn, withScrn, heightScrn)
gradient = graphics.newGradient(
{ 80, 211, 255 },
{80, 100, 180 },
“up” )
backgroundfill:setFillColor(gradient)
see this post: http://www.christianpeeters.com/complete-app-tutorial/rolly-bear-worlds-main-navigation-structure/
The white rectangle somehow gives me the idea that the coordinates of you background is not set the right way.