Welcome to the latest installment in the Endless Runner series. In today's tutorial, you will learn how to build a game menu using the Director Class. Let's get started!
Congratulations on coming this far in the series! It has been a lengthy run, but now we have a fully functional game and hopefully a basic understanding of how to add your own components to expand on everything. The step we are going to do now is to put our little level into a menu system so it feels like a full game.
We are going to do this by using the director class, which is an incredibly well made little library by Ricardo Rauber that is going to do all of the work for you. Once you see how laughably easy it is, you will be able to implement this quickly in any project.
The first thing that you need to do is go to here and download the latest version of the class. When you download it, it is going to give you a lot of files. It comes with a lot more than just the director class, including examples, templates, and additional functions that can make your life easier, so feel free to check out everything that it has to offer. If you don't want all of the fun little extras provided, you can simply download the files for this tutorial and grab it from there.
Now that we have the director.lua file, put it in the same folder as your main.lua. Speaking of your main.lua file, go ahead and rename it. It doesn't matter what you change it to because we are going to end up deleting it anyway. After you have done this you are going to make three new files in that folder. Call them main.lua, menu.lua, and game.lua. The basics of how this is going to work is that main.lua is going to get called to start the game. It is going to be the base of our program, while menu.lua and game.lua are going to act as components(displayGroups) that are displayed within a displayGroup inside of the main.lua itself, much the same way all of the different displays groups in our game are all held and displayed from within the screen displayGroup in our current game.
So, first things first, let's set up our new main.lua file. Open it up and plop this code in there:
display.setStatusBar(display.HiddenStatusBar) --by telling Corona to require "director" we are --telling it to include everything in that file, --giving us easy access to its functions. This is --also how you would include any functions or --"classes" that you created in outside files. local director = require("director") local mainGroup = display.newGroup() local main = function() --this creates a view that we will use to load --the other scenes into, so as our game progresses --technically we are staying in the main.lua file --and just loading new views or scenes into it mainGroup:insert(director.directorView) --we tell the director to load the first scene which --is going to be the menu director:changeScene("menu") end --be sure to actually call the main function main()
So that is it! Take away the comments and you will see it only takes about 5 lines to get the director class up and running. As it says in the comments, the way this is actually working is main.lua gets started first, but then instead of having the flow of the game be dictated inside of the main.lua file, we let the director class take over. We create a display.newGroup and reserve it as our main view. So when the game actually loads up, everything that you see is actually being viewed from the main.lua file inside of this display group. Pretty neat, huh! So, once you insert that view into it, we simply need to tell the director which scene to load up and it will automatically take us there. Easy enough, right?
The next thing that we need to do for this to actually work is to have a menu scene. The code is really simple and most of it we have already seen, so I'm going to post the whole file first and then explain the few things that are new. Let's open up menu.lua and see how easy it is to actually create a simple menu system. Copy the following into your menu.lua:
--this line is required so that director knows that --this is a scene that can be loaded into its view module(..., package.seeall) --we need to be able to access the director class of --course so be sure to include this here local director = require("director") local sprite = require("sprite") --everything that you want this scene to do should be --included in the new function. Everytime the director --loads a new scene it will look here to figure out what --to load up. new = function( params ) --this function will be returned to the director local menuDisplay = display.newGroup() --everything from here down to the return line is what makes --up the scene so... go crazy local background = display.newImage("background.png") background.x = 240 background.y = 160 local spriteSheet = sprite.newSpriteSheet("monsterSpriteSheet.png", 100, 100) local monsterSet = sprite.newSpriteSet(spriteSheet, 1, 7) sprite.add(monsterSet, "running", 1, 6, 600, 0) local monster = sprite.newSprite(monsterSet) monster:prepare("running") monster:play() monster.x = 60 monster.y = 200 local monster2 = sprite.newSprite(monsterSet) monster2:prepare("running") monster2:play() monster2.x = 420 monster2.y = 200 monster2.xScale = monster2.xScale * -1 local title = display.newImage("title.png") title.x = 240 title.y = 80 local playButton = display.newImage("playButton.png") playButton.x = 240 playButton.y = 220 menuDisplay:insert(background) menuDisplay:insert(monster) menuDisplay:insert(monster2) menuDisplay:insert(title) menuDisplay:insert(playButton) --this is what gets called when playButton gets touched --the only thing that is does is call the transition --from this scene to the game scene, "downFlip" is the --name of the transition that the director uses local function buttonListener( event ) director:changeScene( "game", "downFlip" ) return true end --this is a little bit different way to detect touch, but it works --well for buttons. Simply add the eventListener to the display object --that is the button send the event "touch", which will call the function --buttonListener everytime the displayObject is touched. playButton:addEventListener("touch", buttonListener ) --return the display group at the end return menuDisplay end
One thing that is new here is the director:changeScene() line. It's pretty straight forward as it does exactly what you would think it would. However, I wanted to point out the secondparameter, the "downFlip". There are a lot of built in transitions that can make your game look all fancy when switching between scenes, it just depends on what you want your game to look like. Here is an image of our transition in progress.
Be sure to try out some of the different transitions to find the one that will work best for your game. You can find a full list of them in the director.lua file. Just look at all of the different effects and give them a whirl. A couple more examples you can try quickly if you don't want to go digging are "crossfade", "overfromtop", and "flipFromBottom" to name just a few! So, aside from that, there is very little in there that you haven't seen before. All of that stuff is just making a cheap little main menu screen. There are a couple things in there that you should take note of that will be required in every scene that the director needs. The first being:
Plain and simple. You need that exact line at the top of every *.lua file that the director is going to use as a scene. After this you have the new() function that is called when entering the scene and everything that you want the scene to do should be included here. The first thing you need to do is make a displayObject that will be returned to the director to be displayed, then just make sure to return it at the end of the new function. That is all. Sound easy? That's because it is! The director class really makes it that easy. As long as your images are in the right spot for the menu scene you should be able to run the main.lua file and have it start up without any problems.
So far we have our main file and our main menu file taken care of. The last thing that we need to do is make some changes to our game file. This is also going to be easy. Open up the game.lua file and add the following code:
module(..., package.seeall) local director = require("director") new = function( params ) local gameDisplay = display.newGroup() --paste code here return gameDisplay end
That is the barebones of what you need for a scene. Incidentally, this is all you need to get your old main.lua file into your game.lua file. Once you have that in there, the only thing you have to do is copy your entire old main.lua file and paste it where it says paste code here. Do that and you are done. . .almost. We are also going to make some changes to how we display our game over message. Rather than having one large button, we are going to break it up into several smaller buttons that let us choose to restart the game or go back to the menu. Let's start making some of those changes (note that gameOver.png was changed to fit the new buttons layout. The dimensions of the image is still the same so you shouldn't have to make any other adjustments to it). The first changes are going to be to add the new buttons in the game.
Add this code to where we add all the other images at the top of the file:
local yesButton = display.newImage("yesButton.png") yesButton.x = 100 yesButton.y = 600 local noButton = display.newImage("noButton.png") noButton.x = 100 noButton.y = 600
Then be sure to add them both to the screen as well. Add these after the rest of the screen inserts so they appear on top of everything else:
The next thing you will want to do is go to the touched() function and remove the first if/else statement so that it looks like this:
function touched( event ) if(monster.isAlive == true) then if(event.phase == "began") then if(event.x < 241) then if(onGround) then monster.accel = monster.accel + 20 end else for a=1, blasts.numChildren, 1 do if(blasts[a].isAlive == false) then blasts[a].isAlive = true blasts[a].x = monster.x + 50 blasts[a].y = monster.y break end end end end end end
By doing this it makes the touched function one that purely handles in-game interactions. We are going to move the logic that restarts the game to its own function. Add these functions below the touched() function:
function noListener(event) director:changeScene( "menu", "downFlip" ) return true end function yesListener(event) restartGame() return true end
So, pretty straight forward, and the same thing that we have done several times now. Next, shoot down to the bottom of the code where we add our event listeners and add two more listeners:
yesButton:addEventListener("touch", yesListener ) noButton:addEventListener("touch", noListener )
So, the last few things we have to do is account for the changes in position to the two buttons when our monster dies, and when we reset the game. Here are the additions we need to include to make sure they work correctly. In the restartGame() function add this:
noButton.x = 100 noButton.y = 600 yesButton.x = 100 yesButton.y = 600
Then go to the checkCollisions() function, where you should see these lines several times:
gameOver.x = display.contentWidth*.65 gameOver.y = display.contentHeight/2
Add this block right under each instance of that (it should be 4 all in all, one for each way to die):
yesButton.x = display.contentWidth*.65 - 80 yesButton.y = display.contentHeight/2 + 40 noButton.x = display.contentWidth*.65 + 80 noButton.y = display.contentHeight/2 + 40
Once you have all of those changes in place you should be able to start the game from main.lua and be able to move from your main menu to your game, die, and then choose whether you want to play again or to go back to the menu. So, assuming all is well, that is the end, we now have our game moved into a menu system using the director class! We moved very quickly through most of that, the reason being is that there really wasn't very much new content. If you have any problems with any of the steps, let me know in the comments and I will be more than happy to help you work through them. The new folder in the downloadable file also contains the finished product as well so you can always use that as a reference. Hopefully this tutorial series has been beneficial for you in geting you started in game development with the Corona SDK, as you can see the process overall is really very simple. The Corona SDK is powerful and very user friendly once you get the basics down. Hopefully, you can now move forward and make some awesome games.
There will be one more tutorial for this series, but it will not be changing the game in anyway, but will be focusing on submitting your game to the app store as that is always an adventure doing it your first time. So happy coding and thanks for following along!