As of right now we have exactly nothing, so anything is an improvement. Let’s start our game journey off with a bang… or at least a window. Without further ado, some code:
screenWidth = MOAIEnvironment.screenWidth screenHeight = MOAIEnvironment.screenHeight print("Starting up on:" .. MOAIEnvironment.osBrand .. " version:" .. MOAIEnvironment.osVersion) if screenWidth == nil then screenWidth =640 end if screenHeight == nil then screenHeight = 480 end MOAISim.openWindow("Window",screenWidth,screenHeight) viewport = MOAIViewport.new() viewport:setSize(screenWidth,screenHeight) viewport:setScale(screenWidth,screenHeight) layer = MOAILayer2D.new() layer:setViewport(viewport) MOAIGfxDevice.setClearColor(1,0.41,0.70,1) MOAIRenderMgr:pushRenderPass(layer)
Save that code to a file named main.lua and you have just successfully created your first Moai application! Well, unless of course you have already created Moai applications before coming here… in which case we have just created our first Moai application! Huzzah!
Now run it. If you followed my earlier tutorial about setting up IntelliJ to work with Moai, it is simply a matter of selecting Tools->Moai->Run Moai. If you added gluthost.exe to your path, you can also run by opening a command prompt, navigating to the folder you saved main.lua to and typing moai main.lua. If you’ve done neither of these things, well, you are on your own about figuring out how to run it.
Anyways, here it is running in all of its glory!
It’s a window, its 640 pixels wide, 480 pixels high and its pink. Hot pink to be specific.
We all start somewhere.
Whatcha talkin ‘bout Willis?
So, what exactly have we done here?
Well, lets take it from the top.
screenWidth = MOAIEnvironment.screenWidth screenHeight = MOAIEnvironment.screenHeight
Here we are simply getting the screen width and height from the global MOAIEnvironment object. MOAIEnvironment stores a ton of information about the platform it is being run on when the application starts up. Considering that Moai code can run on PC, Mac, iOS and Android, details about your environment are obviously quite useful.
MOAIEnvironment currently holds the following values:
- appDisplayName
- appID
- appVersion
- cacheDirectory
- carrierISOCountryCode
- carrierMobileCountryCode
- carrierMobileNetworkCode
- carrierName
- connectionType
- countryCode
- cpuabi
- devBrand
- devName
- devManufacturer
- devModel
- devPlatform
- devProduct
- documentDirectory
- iosRetinaDisplay
- languageCode
- numProcessors
- osBrand
- osVersion
- resourceDirectory
- screenDpi
- screenHeight
- screenWidth
- udid
Additionally MOAIEnvironment has methods for generating a GUID ( a big unique identifier ), getting the network MAC address ( not exactly sure why this got it’s own function ) as well as for setting new values. That said, the above list is pretty self explanatory, so lets move on.
print("Starting up on:" .. MOAIEnvironment.osBrand .. " version:" .. MOAIEnvironment.osVersion)
Dump the OS name ( “Mac/Windows/Android/etc” ) and Version ( “10.5/XP/etc”). Interesting note, I am running on Windows 8 Consumer preview and version doesn’t exactly work well. So, why did I do this? Just because… I suppose it might be of interest to see how easy it is to put debug information to the console.
if screenWidth == nil then screenWidth =640 end if screenHeight == nil then screenHeight = 480 end
Here we are checking to see if our screenWidth and screenHeight values returned from MOAIEnvironment are nil or not. If they are in fact nil, we set them to a default of 640×480 or whatever other resolution you happen to like. This check is actually extremely important in this case as MOAIEnvironment actually returns nil for both of these values when running using the default glut host! It is up to the host to provide the information, so don’t count on it being there!
MOAISim.openWindow("Window",screenWidth,screenHeight)
Finally we create our window, at the full screen resolution if available or 640×480 otherwise with a title of “Window”. Inventive eh? I thought so, that took hours to come up with.
You may be asking yourself “What the hell is a MOAISim”? That there, is a good question! MOAISim is short form for MOAISimulation, it is a global object that we can consider the keeper of all things time related. In a sense it is the heart of your application, but you don’t interact with it all that often. Oh, and it is responsible for opening windows for reasons I’m not exactly sure of ( as well as going full screen ). You should check it out when you have some spare cycles, it contains a hodgepodge of important stuff. For now though, just realize it’s what opens your window, and without a window, we’ve got nothing. Simply put, you have to call MOAISim.openWindow() at some point, period.
Now that we have a window, we now create a viewport into our world. If you are thinking the analogies are getting a bit confused now that our window has a window in it, I suppose that is true. Although, if you remember the Subaru SVX, it’s window had a window in it…
God I loved that car… oh, wait sorry, going off topic here. Don’t blame Moai for the mixed metaphor, the whole window/viewport thing way predates them, so get used to metaphors with windows within windows. Back to the next bit of code:
viewport = MOAIViewport.new() viewport:setSize(screenWidth,screenHeight) viewport:setScale(screenWidth,screenHeight)
Anyways, think of a window as the actual thing you are going to render to, such as the screen of your phone, or the window your application is running in( if on a Mac or PC ). A viewport on the other hand, is a view into your game world. Your world itself can be many times larger than a single window and a viewport controls which portion of it to view at any given time.
Generally, unless you have multiple viewports ( think picture in picture ), you will always size the viewport to the same dimensions as the screen in the setSize() call. setScale() will often be set to the same size as well, if you want pixels in the world to be 1 to 1 with pixels on the screen. setScale is used to scale world pixels to screen pixels. If you’ve got no idea why you would want to do this, or you want your pixels to be the same size on screen as in the world, simply passed the same values into setScale as you did into setSize. If you really want a mindtrip, try to decipher the official documentations explanation!
Sets the number of world units visible of the viewport for one or both dimensions. Set 0 for one of the dimensions to use a derived value based on the other dimension and the aspect ratio. Negative values are also OK. It is typical to set the scale to the number of pixels visible in the this-> This practice is neither endorsed nor condemned. Note that the while the contents of the viewport will appear to stretch or shrink to match the dimensions of the viewport given by setSize, the number of world units visible will remain constant.
I neither endorse, nor condemn trying to make sense of that! Nicely, for 99% of people, you will just use the same value for both and be done with it. Ok, moving on:
layer = MOAILayer2D.new() layer:setViewport(viewport)
So, we’ve got Windows and viewports and now we have layers. If you’ve ever used Photoshop or The GIMP, the concept of a layer should be instantly understandable to you. Way back in the stone age of animation, people making animated movies used to draw on cel(uloid), which were transparent overlays ( or layers! ) over a static background. By merging all of these cels and a static background together, you end up with your finished animation. Consider this diagram I brazenly ripped off from this site:
That diagram pretty much represents how layers work. Your application will have one or more layers ( at least one ), and layers will be composed of stuff ( scientifically correct term ) to be displayed. In our case, that stuff is the color pink… So, anyways… we create a new MOAILayer2D called layer, then apply our viewport to it, telling Moai which portion of our layer to look at.
MOAIGfxDevice.setClearColor(1,0.41,0.70,1) MOAIRenderMgr:pushRenderPass(layer)
And here is where we bring the pink.. er, hot pink. MOAIGfxDevice is another global object referred to as a Singleton. In some circles, that is a swear word, but fortunately not here. Basically a Singleton means it is a global object, so you can use it anywhere, and that, like Highlanders, there can be only one. This of course is where the Single of Singleton comes in. Anyways, MOAIGFXDevice is a singleton representing the graphic’s device ( think GPU ), although to be honest, beyond setting the background color and a few other things, you wont really use it all that much. At least, I don’t think you will.
So, what exactly are the values I passed in to setClearColor anyways? Well those are red, green, blue and alpha values, with values ranging from 0 to 1. Alpha is the level of transparency, with 0 being transparent, while 1 is opaque. If you are wondering how I came up with the values for hot pink, simple… I found an HTML color chart which had all the colors represented as values from 0-255, and simply divided each color value by 255 to get it’s decimal value.
Now on to MOAIRenderMgr, hey, guess what, it’s a singleton too! I guess we know where the ton part of singleton comes from, eh?
Anyways, MOAIRenderMgr is all about controlling what is going to be drawn each frame, and that is managed by pushing a ton of MOAIRenderable objects on it using pushRenderPass. Of course, layer just so happens to inherit from MOAIRenderable. Now, if you currently look at any of the official samples, they actually call MOAISim.pushRenderPass(), but this has been deprecated, meaning if you use it, sometime in the future Moai developers will come into your house and defecate on your rug. Or something like that. Anyways, use MOAIRenderMgr going forward and bad things wont happen to you or your rug.
And that, there, is the end of the first tutorial. As these things we just covered are the absolute fundamentals of every single application, I went into more details than I generally will in the future. This means two things, if you read future tutorials, I expect you to have read and understand the prior tutorials, so I wont be covering things I have already covered. Second, they will be much shorter, meaning less eye strain from you and less finger strain from me.