Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
22. October 2012

This post is going to look at getting your Moai app to run under NaCL, which is Google’s mechanism for allowing you to execute C++ code within Chrome.  There are a number of restrictions, but fortunately Zipline have done most of the hard work for us.

 

Like any other platform, your code is run within a host.  If you are working from the binary ( non-GitHub ) distribution, the host is already built for you and you can skip ahead until you encounter the text “STOP SKIPPING AHEAD!”.  If you are working from Github sources, you need to build the host first.  That is what we are going to do next.

 

Building the Chrome Host

 

First is a matter of locating it.  The source for the NaCL host is located at moai-install-dir/scons/

There are a few things you are going to need to continue…

 

First off, if you haven’t already installed Cygwin, I highly recommend that you do.  The Android  build process basically requires it, so I am going to assume you already have it.  If you don’t, refer to the Android installation guide Cygwin section for details.

You also need to have Python 2.6 or 2.7 installed.  To check, fire up Cygwin terminal and type:

python –V

If you get an error that the command wasn’t found, Python isn’t installed so let’s install it.  The easiest way is to run the Cygwin setup application, then click Next next next until you get to the Select Packages screen.  In the search box enter Python, in the results expand Python and select python: Python language interpreter.

image

Make sure you don’t have any Cygwin Terminal windows open, then click Next and let Cygwin do it’s thing.

 

Now that you have Python installed, we need to download the native client SDK. (That’s the direct download link btw… )

Save it somewhere you can remember.  Open the archive and extract the folder nacl_sdk.  I went with c:\dev\nacl_sdk, but choose whatever you want, just be sure to update your paths accordingly.

Now open a Cygwin terminal window and change in to the nacl sdk directory, which in my case is:

cd /cygdrive/c/dev/nacl_sdk/

Now we want to run the installer/downloader.  In the terminal window type:

./naclsdk update pepper_17

Even though the current version is 22, you need to install 17, as it ships with developer tools Moai depends on.  For some reason, Scons has been removed from future versions.  That is what the above command does, gets and attempts to install pepper version 17.



 

DEALING WITH GOOGLE DEVELOPER TOOLS GOTCHA BELOW!

OK, here’s the thing, we are dealing with Google developer tools, and Google developer tools are always broken in some way, especially on Windows, naclsdk is of course no exception.  After running the above command you will be greeted with the following error:

-------------------------------------------------------------------------------------------------

Updating bundle pepper_17 to version 17, revision 112997
Traceback (most recent call last):
  File "/cygdrive/c/dev/nacl_sdk/sdk_tools/sdk_update_main.py", line 759, in <module>
    sys.exit(main(sys.argv[1:]))
  File "/cygdrive/c/dev/nacl_sdk/sdk_tools/sdk_update_main.py", line 752, in main
    InvokeCommand(args)
  File "/cygdrive/c/dev/nacl_sdk/sdk_tools/sdk_update_main.py", line 741, in InvokeCommand
    command(options, args[1:], config)
  File "/cygdrive/c/dev/nacl_sdk/sdk_tools/sdk_update_main.py", line 583, in Update
    UpdateBundle()
  File "/cygdrive/c/dev/nacl_sdk/sdk_tools/sdk_update_main.py", line 564, in UpdateBundle
    RenameDir(bundle_move_path, bundle_path)
  File "/cygdrive/c/dev/nacl_sdk/sdk_tools/sdk_update_common.py", line 56, in RenameDir
    shutil.move(srcdir, destdir)
  File "/usr/lib/python2.6/shutil.py", line 260, in move
    copy2(src, real_dst)
  File "/usr/lib/python2.6/shutil.py", line 95, in copy2
    copyfile(src, dst)
  File "/usr/lib/python2.6/shutil.py", line 50, in copyfile
    with open(src, 'rb') as fsrc:
IOError: [Errno 2] No such file or directory: u'/cygdrive/c/dev/nacl_sdk/pepper_17_update'

YAY!  Don’t worry, it’s pretty easy to work around.  The installer is trying to execute a program that doesn’t exist, but the installer was downloaded as part of the above process.  Go in to the folder sdk_cache and locate the file naclsdk_win.exe and run it.  When prompted for an install path, install it to your NACL_SDK folder/pepper_17.  In my case that means C:\dev\nacl_sdk\pepper_17

Now we need to set an environment variable with the path to the NACL SDK.

setx NACL_SDK_ROOT /cygdrive/c/dev/nacl_sdk

Keep in mind, the setx command requires administrator rights, so be sure to run your cygwin terminal as administrator if you aren’t already.

Now the bummer part, exit and restart Cygwin terminal, system level environment variables don’t take immediate effect.

 

Are we there yet? Nope… it’s FMOD install time

Close… one more dependency left… FMOD.  FMOD is a commercial audio system ( AKA, if you ship a product, you’ve got to pay to use it ).  With most of Moai, you can get by using the free Untz audio system, but with NaCL, FMOD is required.  So you either have to gut the FMOD library from the build dependencies or download and configure FMOD.  I’ve opted for the second ( audio is after all, kind of nice! ), but either option is open to you.

Head on over to the FMOD download page and download the archive for FMOD for Google Native Client. Unfortunately you need to download a version that supports the same Chrome version as Moai (17).  The following direct link will download the correct version. (Direct linkIT IS VERY IMPORTANT YOU DOWNLOAD THIS VERSION…. just so you know.

Save and extract that archive somewhere.  This file is a tar.gz, so if you are using a program such as 7zip, you need to extract it, then extract the file you just extracted.  I took the resulting folder, renamed it fmodchrome and copied it to c:\dev\.  The resulting directory should look like:

image

Now we need to set yet another environment variable, one named FMOD_CHROME_SDK_ROOT and pointing at this new directory.  Once again in Cygwin terminal type:

setx FMOD_CHROME_SDK_ROOT /cygdrive/c/dev/fmodchrome

Once again, you need to exit and restart Cygwin terminal for this variable to take effect.

 

It’s building time!

 

At this point in time, there seems to be a problem with the scons build script so that the paths ../3rdparty and ../src aren’t working, at least, not on Windows.  The following is a brutal hack, and I will post a better solution when I come up with it.  For now, we simply copy all the source into the scons folder.  Copy the contents of [moaifolder]/src, [moaifolder]/3rdParty and [moaifolder]/scons/src to the scons directory.

Now cd in to the maoi scons directory, on my pc /cygdrive/c/dev/moai-dev/scons and run

./build.sh

Hopefully all went well.  If you get errors… something didn’t go so well… if you want, just skip ahead and download the version I compiled.  You only really need the build process working if you intend to alter the host.

 

Now copy the following files to a new folder:

moai.nmf

moai_x86_32.nexe

maoi_x86_64.nexe

 

This is your Moai Host ready to go. 

 

If for some reason you couldn’t get your host to build, you can download mine.

 

STOP SKIPPING AHEAD!

 

Packaging your app to run in Chrome

Now you need to package your application up into Chrome friendly goodness.  The steps are fairly straight forward

In the folder you copied the .nmf and .nexe files, create a new file called manifest.json here is what I put in mine:

manifest.json

{
    "name":"moai",
    "version":"42",
    "app": {
        "launch": {
          "local_path": "moai.html"
        }
      }
}

Now you need an html file to actually host your application. As you probably guessed by the manifest file, I called mine moai.html:

moai.html

<!DOCTYPE html>
<html>

<head>
<body>
  <title>Hello Moai!</title>
  <div>
    <embed name="nacl_module"
           id="moai"
           width=480 height=320
           src="moai.nmf"
           type="application/x-nacl" />
  </div>

</body>
</html>

Finally you need your Moai application ( the lua bits ).  Just copy your project sources into the same directory, just be sure a file is called main.lua, this is your app entry point and will automatically be called the the host.  Here for example is my folder:

image

I simply grabbed the sources from this tutorial.

 

 

Configure Chrome to run your app

Now you need to let Chrome know you want to enable NaCL applications.  In Chromes location bar enter chrome://flags, the following window should appear.

image

Scroll down and enable Native Client as shown by the arrow. You need to restart Chrome for this to take effect!

So, um, restart Chrome.

 

Now you need to add your application.  To do this, in Chrome, drop down the Menu and select Tools->Extensions.

 

image

In the resulting window, enable Developer Mode, then click Load unpacked extension…

 

In the browse dialog, navigate to the folder you’ve saved everything in then click OK.

image

 

Your extension should now be installed.  Launch a new tab ( CTRL+N ) in Chrome, and at the bottom of the screen, select Apps

image

 

Your app icon should appear on the page:

image

 

Click it.

 

Voila, a Moai application running in Chrome:

image

 

Enjoy.

Programming


12. October 2012

 

If you followed my Moai tutorial series, you may recall in this post I mentioned that Android keyboard support currently didn’t exist, although there is an effort underway to fix that.  Well, a user over on the Moai forums got sick of waiting and implemented a soft keyboard using MoaiGui, which I covered in this post.

 

It is written as a Lua module and the only dependency is you need to pass in your Moaigui object.  Here is the code:

--utility function
function distance ( x1, y1, x2, y2 )
        return math.sqrt ((( x2 - x1 ) ^ 2 ) + (( y2 - y1 ) ^ 2 ))
end

k = {}

local kboard = nil
local keysWide = 1
local keySize = 5
local boardHeight = keySize
local left = 50 - keySize/2
local top = 100
local xpos = left
local ypos = top
local rowCount = 0
local speed = 1000
local textbox = nil

--release all gui objects
k.destroyKeyboard = function (self, gui)
        if kboard ~= nil then
                for k, v in pairs(kboard) do
                        gui:destroyWindow(v)
                end
                kboard = nil
        end
end

--setup a new keyboard, starting with only 1 row
k.createKeyboard = function (self, gui, charsWide)
        if kboard ~= nil then destroyKeyboard(gui) end
        kboard = {}
        keysWide = charsWide
        boardHeight = keySize
        left = 50 - keySize*charsWide/2
        xpos = left
        top = 100
        ypos = top
        rowCount = 0
end

 

local function keyClick(event, data)
        --give focus to selected textbox so that it receives the keypress
        if textbox ~= nil then data.g:setFocus(textbox) end
        --pass keypress to gui
        data.g:injectKeyDown(data.character)
        data.g:injectKeyUp(data.character)
end

 

k.addKey = function (self, gui, char)
        if kboard == nil then return end
        --empty string indicates skipping a key slot
        if char ~= "" then
                kboard[char] = gui:createButton()
                kboard[char]:setPos(xpos, ypos)
                kboard[char]:setDim(keySize, keySize)
                kboard[char]:setText(char)
                data = {}

                data.g = gui
                if char == "<" then
                        data.character = 8 --backspace
                else
                        data.character = string.byte(char) --ascii value of character
                end

                kboard[char]:registerEventHandler(kboard[char].EVENT_BUTTON_CLICK, nil, keyClick, data)
        end
        --increment to next key in row
        rowCount = rowCount + 1
        xpos = xpos + keySize
        if rowCount >= keysWide then
                --new row
                xpos = left
                ypos = ypos + keySize
                boardHeight = boardHeight + keySize
                rowCount = 0
        end
end

 

--moves the key to its place at the bottom of the screen (or off the screen if show=false).
--NOTE: only returns once the move has finished
local function moveKey(key, show)
        local x, y = key:getPos()
        --move onto the screen
        local newy = y - boardHeight
        --move off the screen
        if not show then newy = y + boardHeight end
        --not really the best way of getting the target location, but the easiest/quickest solution
        key:setPos(x, newy)
        local tx, ty = key._rootProp:getLoc()
        key:setPos(x,y)
        --calculate a travel time relevant to the distance being traveled
        local travelTime = distance(x, y, x, newy) / speed

        MOAIThread.blockOnAction(key._rootProp:seekLoc(tx, ty, travelTime, MOAIEaseType.LINEAR))
        --the seekLoc only moves the prop, the prop container is not aware of the move, so tell it.
        key:setPos(x, newy)
end

 

--moves all keys to the desired location
k.showKeyboard = function (self, gui, show)
        if kboard == nil then return end
        --only need to pass in show when you want to hide the keyboard
        if show == nil then show = true end
        for k, v in pairs(kboard) do
                --move keys in separate threads
                MOAIThread.new():run(moveKey, v, show)
        end
end

 

k.hideKeyboard = function (self, gui)
        self:showKeyboard(gui, false)
end

k.setTextbox = function(self, tbox)
        textbox = tbox
end

return k

 

 

And here is some code demonstrating the keyboard in action:

 

local kb = require "keyboard"

kb:createKeyboard(gui, 11)
keys = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "<",
        "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "",
        "", "a", "s", "d", "f", "g", "h", "j", "k", "l",  "",
        "", "", "z", "x", "c", "v", "b", "n", "m"}
for k, v in pairs(keys) do
        kb:addKey(gui, v)
end

kb:showKeyboard(gui)

local editBox = nil
local function handleEditBoxGainFocus(self, event)
        self._cursorPos = #self._internalText + 1
        self:_addCursor()
        kb:setTextbox(editBox)
        return self:_baseHandleGainFocus(event)
end

editBox = gui:createEditBox()
editBox :setPos(40, 30)
editBox :setDim(20, 5)
editBox :setText("")
editBox ._onHandleGainFocus = handleEditBoxGainFocus

 

 

None of this code is mine, all of the credit goes to lancew over on the Moai forums.  The only thing I have done is slightly changed the formatting to make it a bit more legible. You can read the original thread right here.

 

Great work Lance!

Programming


3. October 2012

 

Not to make any assumptions about your mother’s computing abilities of course!  My mother is flummoxed by powering a computer on and its all down hill from there, so perhaps not quite easy enough *my* mother could do it.  But it is easy, really easy.  Really.  Easy.  This is just one of those areas Moai really shines.

 

If you are unfamiliar with the concept, a tilemap is pretty straight forward.  You have an image or series of images that hold the art for your game.  In this example, we are going to use a regular top down ( not isometric ) perspective and our image map is composed of tiles 32x32 pixels in size.  Your game map is then made up of a grid that says “at this location, draw this tile”.

 

First, the topic of the image itself.  That one, in the spirit of open source, I am ripping off someone else’s work! Smile  You may remember a while back Mozilla released a completely free HTML5 based MMO, BrowserQuest.  Well, we are going to make use of one of the tilesheets from that project.  This is the file right here (it is big).  This is massive overkill, but I like massive overkill.  Here is a shrunk down version of the image we are going to be working with:

image

 

The image is composed of 20 columns of 32x32 images and 98 rows of them, for a grand total of 1,960 tile images or cells, in a single image.  So, that covers our image, let’s take a look at the code:

screenWidth = MOAIEnvironment.screenWidth
screenHeight = MOAIEnvironment.screenHeight
if screenWidth == nil then screenWidth =1280 end
if screenHeight == nil then screenHeight = 800 end

MOAISim.openWindow("Window",screenWidth,screenHeight)

local viewport = MOAIViewport.new()
viewport:setSize(screenWidth,screenHeight)
viewport:setScale(480,320)

local layer = MOAILayer2D.new()
layer:setViewport(viewport)
MOAIRenderMgr.pushRenderPass(layer)

local map = MOAIGrid.new()
map:initRectGrid(15,10,32,32)
map:setRow(10,0x0b,0x0c,0x0d,0x0b,0x0c,0x0b,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0xc7)
map:setRow(9,0x2e,0x2f,0x30,0x31,0x32,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(8,0x41,0x42,0x43,0x44,0x45,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(7,0x55,0x56,0x57,0x58,0x59,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(6,0x69,0x6a,0x6b,0x6c,0x6d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(5,0x7d,0x7e,0x7f,0x80,0x81,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(4,0x91,0x92,0x93,0x94,0x95,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(3,0xa5,0xa6,0xa7,0xa8,0xa9,0x0b,0x0c,0xc7,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(2,0xb9,0xba,0xbb,0xbc,0xbd,0xc7,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(1,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2)


local mapTiles = MOAITileDeck2D.new()
mapTiles:setTexture("tilesheet.png")
mapTiles:setSize(20,98)

local prop = MOAIProp2D.new()
prop:setDeck(mapTiles)
prop:setGrid(map)
prop:setLoc(-480/2,-320/2)
-- functionally the same as above
-- prop:setPiv(480/2,320/2)

layer:insertProp(prop)

And if we run this code, we see:

 

image

 

Ok, that’s a pretty cool amount of stuff on screen, for not so much code!  Let’s take a look at exactly what we did here.  Once again from the top ( one of these examples I am going to do from the bottom! ).  Once again, we are only going to look at the new bits, so if I fail to cover something, check out the prior tutorials, chances are I covered it then.

 

local viewport = MOAIViewport.new()
viewport:setSize(screenWidth,screenHeight)
viewport:setScale(480,320)

We’ve seen this many times before, but always in the past I’ve set setSize and setScale to the same value, this time I didn’t… why?  Well the biggest reason is, I defined the grid of map values by hand, which was a gigantic pain in the butt for a screen made up of a 15x10 array of tiles ( 480/32==15, 320/32==10 ), let alone a 1280x800 screen!  By setting the viewport to the actual resolution of your device, but setting the scale to your target resolution ( in this case, the iPhone 3g’s native resolution ), MOAI automatically handles the scaling for you.  This is a nice easy way to support a number of different screen resolutions without having to code each one by hand.

 

Speaking of the map values:

local map = MOAIGrid.new()
map:initRectGrid(15,10,32,32)
map:setRow(10,0x0b,0x0c,0x0d,0x0b,0x0c,0x0b,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0xc7)
map:setRow(9,0x2e,0x2f,0x30,0x31,0x32,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(8,0x41,0x42,0x43,0x44,0x45,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(7,0x55,0x56,0x57,0x58,0x59,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(6,0x69,0x6a,0x6b,0x6c,0x6d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(5,0x7d,0x7e,0x7f,0x80,0x81,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(4,0x91,0x92,0x93,0x94,0x95,0x0b,0x0c,0x0d,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(3,0xa5,0xa6,0xa7,0xa8,0xa9,0x0b,0x0c,0xc7,0xc7,0x0c,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(2,0xb9,0xba,0xbb,0xbc,0xbd,0xc7,0xc7,0xc7,0xc7,0xc7,0x0d,0x0b,0x0c,0x0d,0x0b)
map:setRow(1,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2,0xc2)

In the immortal words of Douglas Adams DON’T PANIC!  This looks a lot scarier than it actually is.  We are creating our map, which is a variable of type MOAIGrid.  Think of a grid as a 2d array of sorts that holds references to the tiles at each location.  Really that’s about what it does.  So we start of by initializing with a call to initRectGrid() passing in the number of tiles ( 15x10 ) and the width and height of each tile ( 32,32 ).  The next 10 lines are simply populating the grid with calls to setRow(), the first value is the row index, then what follows is a list of 32bit indexes in hexidecimal.  This is where the scary comes in.

 

Basically the top left tile in our image map is at 1 ( 0x01 ) and there are 20 tiles per row in the image we used, so the first image in the second row is therefore at 21 ( row count * row width + offset == 1 * 20 + 1 == 21 ), which is then translated in to hex, which is 0x15. 

 

So, basically we “draw” our map using image locations ( as a 1d index using the formula above, converted to hex ).  The MOAIGrid itself, row 1 is the bottom most row, which is why our grid counts down from 10.  Now let’s take a look at how we created our source image:

 

local mapTiles = MOAITileDeck2D.new()
mapTiles:setTexture("tilesheet.png")
mapTiles:setSize(20,98)

Pretty simple over all.  We create a MOAITileDeck2D.  Remember, a Deck is parlance for an image source, and in this case our source is a single image composed of dozens of tiles.  We set the actual image with a call to setTexture() and pass in our filename tilesheet.png.  We then set it’s size, letting it know it is made up of 20 columns and 98 rows of tiles.

 

local prop = MOAIProp2D.new()
prop:setDeck(mapTiles)
prop:setGrid(map)
prop:setLoc(-480/2,-320/2)
-- functionally the same as above
-- prop:setPiv(480/2,320/2)

Neither MOAITileDeck2D or MOAIGrid are display items, for the we need to create a MOAIProp2D.  We set it’s deck ( or source ) to our newly created MOAITileDeck2D mapTiles and it’s grid to our MOAIGrid map.  Finally we set it’s position with a call to setLoc.  This is an area of some possible confusion ( and why I showed the commented setPiv() as well ), because by default our prop will be at (0,0), which is the dead center of the screen.  However, the pivot point of our prop in the case is actually the bottom left corner of our map.  Therefore, we either need to move the pivot point to the center ( what the commented out code does ), or we position the prop offset from the center of the screen by half the width and height of the viewport.

 

You can actually accomplish a heck of a lot with just a bit of code.  Obviously in your case, you want want to use a tool to generate the MOAIGrid indices, doing it by hand is a PITA ( trust me! ).  Really though, the visual rendering of a top down RPG game just boiled down to a dozen or so lines of code!  Perhaps most impressively, this just scratched the surface of what MOAIGrid can do.

 

Coming Soon

General Programming


29. September 2012

As per this post, Moai 1.3 has been released.  If you have never heard of it, Moai is a cross platform, mobile focused, Lua based gameimage development library written in C++, that is open source and completely free.  A lot of focus was put into MOAISim.setTraceback to enable better error reporting.  The list of announced updates is as follows:

 

Added MD5 Hash Writer to compute a hash of a stream as it writes
Added Crittercism crash reporting for Lua stack traces, See samples/util/util-crash-report
Fixed network reachability initialization on iOS
Fixed input bug with multiple presses in one 'Frame'
Android - Fixed incorrect accelerometer data on tablets
Android - Added C-Ares ( Fixes DNS lookup blocking on HTTP calls )
Android -Added MOAILocationSensor
Android - Added MOAICompassSensor
Android - Fixed status bar issues with 3.1 tablets
iOS - Updated Facebook SDK to 3.0
iOS - Updated Crittercism SDK
MOAIApp - Added getUTCTime to Mobile
MOAILayer - Fixed bugs where MOAILayer:setVisible had no effect
MOAIHttpTask - Added 'getProgress' function to HTTPTask for Curl
MOAIJsonParser - Fix for issue with 64bit ints
MOAIFmodEx fixes for 'setLooping'

 

You can learn more about Moai right here or you can check out the tutorials on this very site.

News


27. September 2012

 

The author contacted me a few weeks up with a heads up that he had a Moai friendly Lua IDE in development and I said I would look into it… and well, I didn’t.  Things came up, then something else and something else and frankly it got pushed into the back of my mind.  In the end, that was a good thing too, as since that point in time the author has added auto-completion… a critical feature.

 

As of writing though, this feature is only available from the github sources.  So long as you have cygwin configured ( assuming you are working with Moai, you should already ), and git installed, the process is fairly simple:

 

cd /cygdrive/c/folder/to/install/to

git clone https://github.com/pkulchenko/ZeroBraneStudio.git

 

This will create a folder ZeroBraneStudio with all the appropriate files. 

EDIT (29/09/2012): Author made update, should no longer need to set permissions described below

There is another catch, git mangles the executable permissions on Windows.  The long answer is well… long, the hackishly easy answer is to run:

chmod a+x –R ZeroBraneStudio

 

There is one last catch, you need to set the MOAI_BIN environment variable to point to your MOAI host folder, either that or add it to your PATH variable.  ( I suggest the former ).  To set the MOAI_BIN path, simply open a command prompt and type:

setx MOAI_BIN c:/path/to/moai/host

 

You can of course set the environment variables using the System control panel, but this will require you to reboot your computer for the change to take effect.  SetX simply requires you close the console to take effect.

 

Now you should be able to run the Studio, just double click zbstudio.exe to start it.  Hopefully the author will have a new release soon, that prevents all of this ( except the MOAI_BIN environment variable that is ).  Finally run ZeroBrane and this is what you see:

 

image

 

 

So then… why?  What does ZeroBraneStudio bring to the table that SublimeText or IntelliJ don’t have?

 

That’s an easy one.  I mentioned the first one earlier:

 

image

 

Auto-completion!  After years of using Visual Studio, this one is a gigantic must for me.  Granted as I covered earlier, IntelliJ can be configured to supported auto completion.  But ZeroBrane Studio has a massive head up on IntelliJ, watch this!

 

image

 

This you cannot do with IntelliJ, and it is a gigantic advantage.  But auto-completion isn’t the only thing ZB brings to the table, ZeroBrane Studio has the ability to debug running code!  You can set breakpoints, step in/over and out, set watches and do dynamic expression evaluations, pretty much most of what you expect to see in a debugger. Oddly enough, this needed a Windows Firewall exception.

 

To debug your code, select a line of text to set a breakpoint then hit F9 to create a breakpoint ( a red stop sign will appear in the margin ).  Then select Project->Start Debugging Server.  Then select Project->Start Debugging or hit F5 to run your code and voila:

image

 

Code execution stops at your breakpoint.  Use F10, Ctrl+F10 and Shift+F10 enable you to navigate through your code. You can hover your mouse over a variable to inspect it’s value.  You can also right-click a variable and create a watch window like this:

image

Unfortunately you can’t drill down into an individual Lua table, but hopefully this feature gets added soon!

 

Perhaps coolest of all, ZeroBrane Studio enables REPL interaction.  You can open the console and interact with your code directly:

image

 

As you can see, you can interact with your values in real time, and here are the results in the output:

image

 

It also has the ability to analyze your code, and show you the plethora of stupid mistakes you are making:

image

 

Not everything is perfect.  As mentioned earlier, watch expressions cant drill down into Lua tables, hopefully something that will change soon.  Additionally, it would be nice if the Moai host could be added using a configuration setting instead of using MOAI_BIN, as it makes it difficult to change between runtimes ( moai, moai-untz, release, debug, etc… ), but this is a minor point.  It would also be nice to be able to control more via context menu’s ( add breakpoint, step over/in, etc ). The code syntax highlighting could be improved a bit as well.  Of course, as a straight code editor, it isn’t going to give IntelliJ or SublimeText a run for it’s money just yet.

 

 

That said, this is an absolutely invaluable tool for people working with Moai or LOVE, the debugger and full auto-completion guarantee that this is going to be a stable part of my toolset going forward.  Hopefully the author is able to add more detailed debugging information soon.  If you are working in Moai or LOVE, you owe it to yourself to check out ZeroBrane Studio.

 

Speaking of which, you can get more information here.  Keep in mind, MOAI auto-completion is currently only available in the source distribution, at least as of version 0.32.  Hopefully the new release is bundled soon.

 

EDIT:

I have spoken with the author, and it appears a few of my issues aren’t issues after all.

The system will check for Moai executable in your PATH environment
variable. You can also set "path.moai = 'd:/lua/moai/moai'" in
cfg/user.lua (see cfg/user-sample.lua for example).

 

EDIT2:

ZeroBrane actually can drill down details into a Lua table, in my relative Lua newbishness, I made a pair of mistakes.  First off, it only works on local variables.  Second, Moai does not return tables, it returns Userdata, which ZeroBrane currently can’t parse ( fingers crossed he figures out a way! ).

 

image

 

Here is the behaviour I was expecting to see, and exactly what I got once I defined the table as a local.

General Programming


GFS On YouTube

See More Tutorials on DevGa.me!

Month List