Subscribe to GameFromScratch on YouTube Support GameFromScratch on Patreon
15. February 2015

 

LÖVE, a Lua based game engine I covered wayyyyy back in Battle of the Lua Game Engines and also featured in the game development for kids article, just announced release 0.9.2 just in time for Valentines day.  The announcement follows:

 

Here are a few big changes and highlights, and the full changelog, as well as download links can of course be found below, and on the wiki and front page respectively.


The full changelog is listed here:
  * Added Lua 5.3's UTF-8 module (via utf8 = require("utf8")).
  * Added Shader:getExternVariable.
  * Added several new canvas texture formats.
  * Added love.graphics.getCanvasFormats.
  * Added love.graphics.getCompressedImageFormats.
  * Added ParticleSystem:setQuads.
  * Added ParticleSystem:setLinearDamping.
  * Added SpriteBatch:flush.
  * Added love.graphics.getStats.
  * Added "mirroredrepeat" wrap mode.
  * Added love.audio.setDopplerScale and love.audio.getDopplerScale.
  * Added optional duration argument to Joystick:setVibration.
  * Added love.joystick.loadGamepadMappings and love.joystick.saveGamepadMappings.
  * Added Joint:setUserData and Joint:getUserData.
  * Added Joint:getBodies.
  * Added GearJoint:getJoints.
  * Added Contact:getFixtures and Body:getContactList.
  * Added Body:getWorld.
  * Added Body:getJointList.
  * Added Body/Contact/Fixture/Joint/World:isDestroyed.
  * Added love.mousemoved event callback.
  * Added love.mouse.setRelativeMode and love.mouse.getRelativeMode.
  * Added Scancode enums, love.keyboard.getKeyFromScancode, and love.keyboard.getScancodeFromKey.
  * Added love.window.getDisplayName.
  * Added love.window.minimize.
  * Added love.window.maximize.
  * Added love.window.showMessageBox.
  * Added 'refreshrate' field to the table returned by love.window.getMode.
  * Added love.window.toPixels and love.window.fromPixels.
  * Added love.window.setPosition and love.window.getPosition, and 'x' and 'y' fields to love.window.setMode and t.window in love.conf.
  * Added love.filesystem.isSymlink, love.filesystem.setSymlinksEnabled, and love.filesystem.areSymlinksEnabled.
  * Added love.filesystem.getRealDirectory.
  * Deprecated SpriteBatch:bind and SpriteBatch:unbind.
  * Deprecated all uses of the name 'FSAA' in favor of 'MSAA'.
  * Deprecated the 'hdrcanvas' graphics feature enum in favor of getCanvasFormats.
  * Deprecated the 'dxt' and 'bc5' graphics feature enums in favor of getCompressedImageFormats.
  * Fixed crashes when love objects are used in multiple threads.
  * Fixed love.filesystem.setIdentity breaking in some situations when called multiple times.
  * Fixed the default love.filesystem identity when in Fused mode in Windows.
  * Fixed love.system.openURL sometimes blocking indefinitely on Linux.
  * Fixed love.joystick.setGamepadMapping.
  * Fixed the order of vertices in ChainShapes.
  * Fixed love.mouse.getPosition returning outdated values if love.mouse.setPosition is used in the same frame.
  * Fixed love.graphics.newFont to error when given an invalid size argument.
  * Fixed the filename and backtrace given when love.graphics.print errors.
  * Fixed a small memory leak if love.graphics.newCanvas errors.
  * Fixed shader:getWarnings returning unnecessary information.
  * Fixed some cases of noncompliant shader code not properly erroring on some nvidia drivers.
  * Fixed a potential crash when Shader objects are garbage collected.
  * Fixed a potential small memory leak triggered when love.graphics.newShader errors.
  * Fixed love.graphics.newMesh(vertexcount, ...) causing the Mesh to do instanced rendering.
  * Fixed Mesh:getVertexMap.
  * Fixed Image:refresh generating mipmaps multiple times if mipmap filtering is enabled.
  * Fixed Image:setMipmapFilter to not keep bad state around if it errors.
  * Fixed Mesh:setDrawRange when the Mesh has a vertex map set.
  * Fixed internal detection of the 'position' and 'effect' shader functions.
  * Fixed Texture memory leak when Meshes are garbage collected.
  * Fixed the default line join mode to be 'miter' instead of an undefined value.
  * Fixed the default error handler text size when highdpi mode is enabled on a Retina monitor.
  * Fixed the default error handler background color when sRGB mode is enabled for the window.
  * Fixed love.window.setMode to fall back to the largest available mode if a width or height greater than the largest supported is specified and fullscreen is used.
  * Fixed the state of wireframe mode when love.window.setMode is called.
  * Fixed Canvas:getPixel to error if the coordinates are not within the Canvas' size.
  * Fixed detection of compressed textures to work regardless of the file's extension.
  * Renamed all cases of FSAA to MSAA. The FSAA names still exist for backward-compatibility.
  * Updated the Windows executable to automatically prefer the higher performance GPU on nvidia Optimus systems.
  * Updated the --console command-line argument in Windows to open the console before conf.lua is loaded.
  * Updated t.console and the --console command-line argument in Windows to use the existing Console window,if love was launched from one.
  * Updated the love executable to verify that the love library's version matches.
  * Updated the Lua wrapper code for modules to avoid crashes when the module's instance is created, deleted, and recreated.
  * Updated internal code for handling garbage collection of love objects to be more efficient.
  * Updated love's initialization code to trigger a Lua error if love.conf has an error in it.
  * Updated the paths returned by love.filesystem.getSaveDirectory and friends to strip double-slashes from the string.
  * Updated the error message when love.filesystem.write or File:open fails because the directory doesn'texist.
  * Updated the error message when love.math.setRandomseed(0) is attempted.
  * Updated the error message when invalid UTF-8 strings are used in love functions that expect UTF-8.
  * Updated love.physics.newPolygonShape and love.physics.newChainShape to accept a table of vertices.
  * Updated love.physics.newChainShape to error if the number of arguments is invalid.
  * Updated love.thread.newThread to accept a literal string of code directly.
  * Updated love-created threads to use names visible in external debuggers.
  * Updated SpriteBatch:unbind to use less VRAM if the SpriteBatch has the static usage hint.
  * Updated love.graphics.newImage, love.image.newImageData, etc. to leave less Lua-owned memory around.
  * Updated love.graphics.push to accept different stack types to push. Current types are "transform" and "all".
  * Updated love shaders to accept GLSL ES precision qualifiers on variables, although they do nothing.
  * Updated the error message for love.graphics.newShader to be less cryptic if an invalid filename is given.
  * Updated compressed texture loading code to allow BC6 and BC7 compressed textures (if the graphics driver supports them.)

And our downloads, also available on the home page are here:

 

The entire announcement is available on their forums here.  Perhaps most exciting is the 0.10 release, which adds official Android and iOS support.  Features I know many people have been waiting for.  For the record, there are unofficial Android and iOS ports already available.  This move will just make them part of the main distro.  Still a huge development when it happens.

News


31. October 2014

 

Now that we know how to create vector graphics on iPad, let's take a look at the process of getting them into and using them in Codea.  This part is insanely easy, in fact if this was all there was to it, I could cover this entire process in a single Tweet!  Let’s do it.

 

First of course you need to create a vector graphic in some application and save it as PDF to your Dropbox folder.

Then fire up Codea, create a new project with the following code:

function draw()
    background(40, 40, 50)
    sprite("Dropbox:Jet",WIDTH/2,HEIGHT/2)
end

 

Yeah... that's it.  Select the image from your Dropbox, run it and you will see:

 

Untitled5

 

So basically vectors are treated exactly the same as bitmaps as far as Codea is concern.  Well, with a few exceptions… you can scale them as much as you want and never lose visual fidelity.

 

You can scale by passing in the width and height to the sprite call, like so:

sprite("Dropbox:Jet",WIDTH/2,HEIGHT/2,100,100)

 

So then, vectors are easy to load, look great, scale well are pretty easy to draw... perfect no?

Well… no, there is a catch.  Performance.

Let’s take a quick look at how vectors perform, using the following code:

 

-- VectorTest
function setup()
    fontSize(160)
    fill(255, 0, 25, 255)
end

function draw()
    background(40, 40, 50)

    local outString = "fps:" .. math.floor( 1/DeltaTime)
    local dimensions = textSize(outString)
    
    strokeWidth(5)

    for i= 1,100 do
        local x = math.random(WIDTH)
        local y = math.random(HEIGHT)
        sprite("Dropbox:Jet 2",x,y)
    end
    
    text(outString,WIDTH-dimensions/2,80)
end

 

Here it is running on my iPad Air with drawing 100 vectors:

 

1

 

60 FPS is as good as we can get, so so far so good!  Now lets see what happens when we crank the number up to 500.

 

2

Oh… 

 

From my tests, you can draw about 200 vector images before framerate starts to drop down.  You can easily draw twice that many normal sprites without seeing a noticeable drop in framerate.  

 

Right now our simple frame rate counter is changing far too often to be of much use.  So instead I am going to sample and average across minutes.  Basically every frame we take a sampling of framerate, after a second elapses we move that sample to other array, average and display the results.  So basically we sample the data per frame, but average it out over the span of a second.  This should give us a much smoother frame rate total.  Here is the updated code:

 

 

-- VectorTest
frameRateSamples = { 60 }
fpsSampledPerSecond = { 60 }
elapsedTime = 0
    
function setup()
    fontSize(160)
    fill(255, 0, 25, 255)
end


function mean( t )
  local sum = 0
  local count= 0

  for k,v in pairs(t) do
    if type(v) == 'number' then
      sum = sum + v
      count = count + 1
    end
  end


  return math.floor(sum / count)
end

function draw()
    background(40, 40, 50)

    elapsedTime = elapsedTime + DeltaTime
    if(elapsedTime < 1) then
        table.insert(frameRateSamples,math.floor(1/DeltaTime))
    else
        print("Second elapsed")
        table.insert(fpsSampledPerSecond,mean(frameRateSamples))
        framerateSamples = {}
        elapsedTime = 0
        -- ever 360 seconds reset the table so it doesnt grow forever
        if #fpsSampledPerSecond > 360 then
            fpsSampledPerSecond = { 60 }
        end
    end

    local outString = "fps:" .. mean(fpsSampledPerSecond)
    local dimensions = textSize(outString)
    
    strokeWidth(5)

    for i= 1,200 do
        local x = math.random(WIDTH)
        local y = math.random(HEIGHT)
        sprite(“YourImageHere",x,y)
    end
    
    text(outString,WIDTH-dimensions/2,80)
end

    

 

Now when we run it, we get the follow values, after letting each run for over 60 seconds to take the seed value out of the equation:

Image Type FPS at 100 FPS at 250 FPS at 500 FPS at 1000 FPS at 1500 FPS at 2000
Vector 58 38 21 23 15 9
Bitmap 58 37 21 21 17 10

 

Hmmm, that's interesting.  So once the framerate sampling is smoothed out, the performance between vector and bitmap graphics is basically identical.  In both cases too, oddly enough, 500 sprites caused a drop in performance compared to 1,000…  very very very odd.

 

Now let’s take a look at if scaling is a factor.  In this test we will scale the vector down to 32x32, vs drawing a fixed 32x32 sprite and see if it impacts performance.

 

As a 32x32 pixel image, at 2000 sprites, the framerate a solid 57.

As a vector scaled down to 32x32 in the sprite call the framerate at 2000 sprites is 57.

 

There you have it, contrary to my initial findings, the performance between identically sized bitmap and vector images in Codea is about the same.  Internally, Codea must be rasterizing vectors before drawing them.

 

So, why did my initial tests show a massive improvement in bitmap performance over vectors as the count increased?  Well, that’s because I compared apples to oranges… I used a “nearly the same dimensions” graphic for the test, and that was a huge mistake. Something critical you can take away from this experiment is the size of the sprite being blitted has a huge impact on performance, much more so than the type of sprite.  As you saw above, we can blit > 2000 32x32 pixel images without a drop in performance, while the same number of 200x200 pixel images brings it to it’s knees.

 

So, in a nutshell, its equally as valid to use a vector with code than it is a bitmap.  And as you can see, it uses the exact same code.  Oh, and if you are by chance wondering why we were toping out at 57-58FPS instead of 60 you would expect, that is simply because I floored() the results (aka, rounded down) to make the numbers prettier.  Since I was measuring relative performance, not overall performance, the actual framerate really didn’t matter all that much.

Programming


26. October 2014

 

As I mentioned earlier in the introduction to Codea, it does not support Spritesheets out of the box. I also mentioned earlier that it was a fairly simple thing to add. In this tutorial I am going to show how I created a class in Codea for enabling sprite sheets, which can also serve as a bit of an introduction to non-trivial Codea development.

 

A spritesheet is simply a number of sprite graphics that have put together into a single image.  This often results in better performance because of more efficient memory usage, as well as quicker load times and easier asset management.  There area  number of spritesheet creation tools such as TexturePacker which I discussed how to use here and here.  I am also using the spritesheet graphic mentioned in this post.  It’s important to realize that tools like TexturePacker can create very tightly packed sheets that rotate textures to fit them in best.  In this case however, we are expecting our textures to be arranged in a simple grid pattern.

 

Let’s jump right in to Codea and create a new project.  At the home screen simply click the Add New Project button shown below:

IMG 0280

 

In the resulting dialog, name your project, I’m going with SpritesheetDemo.  When done, click Create.  In my case the create button is slightly bugged, in that case, click slightly to the left of it.

IMG 0283

 

This will now bring you to the main code editing page.  It will start with the following simple “Hello World” code:

 

-- SpritesheetDemo

 

-- Use this function to perform your initial setup

function setup()

   print("Hello World!")

end

 

-- This function gets called once every frame

function draw()

   -- This sets a dark background color 

   background(40, 40, 50)

 

   -- This sets the line thickness

   strokeWidth(5)

 

   -- Do your drawing here

 

end

 

 

What we want to do now is add a new class for our Spritesheet, simply click the + icon in the top right corner of the Codea screen:

IMG 0279

 

Now select Create New Class

IMG 0278

 

In the text field, enter Spritesheet as the name:

IMG 0281

 

It will start with an empty class definition, we will replace it will this code:

Spritesheet = class()

 

function Spritesheet:init(img,x,y,rows,cols,frameWidth,frameHeight,frame,totalFrames)

   self.x = x

   self.y = y

   self.frame = frame

   self.totalFrames=totalFrames

 

   self.frameWidth = frameWidth

   self.frameHeight = frameHeight

   self.frameRows = rows

   self.frameCols = cols

   self.frameSize = vec2(self.frameWidth,self.frameHeight)

 

   self.counter = 0

 

   self.mesh = mesh()

   --self.texture = img

   self.mesh.texture = img -- self.texture

   self.mesh:addRect(0,0,self.frameWidth,self.frameHeight)

end

 

function Spritesheet:draw()

 

   self.counter = self.counter + DeltaTime

   if self.counter > 1/30 then

 

   self.frame = self.frame + 1 

   if self.frame > self.totalFrames then

       self.frame=0

   end

 

   self.x = self.x -1

 

 

   colUnit = 1/self.frameCols

   rowUnit = 1/self.frameRows

 

   row = math.floor(self.frame / self.frameRows)

   col = math.floor(self.frame % self.frameCols)

 

   self.mesh:setRectTex(1,

   col * colUnit,

  ( 1-rowUnit) - row * rowUnit,

   colUnit,

rowUnit)

 

       self.mesh:setRect(1,self.x,self.y,self.frameWidth,self.frameHeight)

 

    self.counter = 0

    end

 

   self.mesh:draw()

end

 

function Spritesheet:touched(touch)

 

end

 

A quick explanation of what the above code does.  First off, you create a Spritesheet by passing in the image to use for the sheet, the x and y coordinates to draw the individual sprite at, the number of rows and columns in your sprite grid, the width and height of each row and column, the frame to start on as well as the total number of frames in the sheet.  The rest of the init() method is mostly about storing these values.  The key call is addRect() which you can think of as creating a viewport into the sprite sheet, so we can view a single sprite.  The critical part is we create a mesh.  This is a quad that we apply our texture to.  In the draw() method you will see why this is important.

 

The draw() function has a lot of functionality built in that you obviously wouldn’t want in a more generalize Spritesheet class.  It does however show you how to display a single frame within the sheet and to iterate through each frame.  First we update our internal counter by DeltaTime, which is the amount of time since the last frame was drawn.  We are animating at a fixed rate of 30hz (obviously something you would want to change ), and each time 1/30th a second elapses, we move on to the next frame, unless its the last frame of animation, in which case we go back to first frame.

 

It’s all of the following bit that is the core of how we make the sprite sheet work.  We have created a mesh and applied the image texture to it.  Obviously though, that texture has a number of sprites on it.  What we are doing is finding the coordinates of our current frame of animation within the mesh.  The critical part here is mesh does not use pixel coordinates!  Instead it uses standard GL device coordinates ( similar to UV coordinates ), that start at 0 and go to 1.  The coordinate (0,0) is the bottom left corner of the texture, while (1,1) is the top right.  So we have to map our texture coordinates to pixel coordinates within the image.  This is also a challenge since our frames of animation start at the top left, not bottom left.  This is why we do (1-rowUnit).  In simpler English, colUnit and rowUnit convert from pixel sizes to fractions of 1.0.  For example, if we have 5 columns, each column is then 0.2 in width.  Once we have figured out the location of our individual frame in texture space, we set it using setRectTex().  Finally we set our elapsed time counter back to 0 then draw our updated mesh using mesh:draw().

 

Now let’s take a look at how we actually use spritesheet in code:  Go back to your Main and alter it like so:

 

 

function setup()

   local img = readImage("Documents:birds")

   bird = Spritesheet(img,WIDTH,HEIGHT/2,5,5,240,314,0,21)

end

 

function draw() 

   background(40, 40, 50)

   bird:draw()

end

 

function touched(touch)

   bird:touched(touch)

end

 

Here we simply load our spritesheet from the local pictures ( or use dropbox, wherever you wish ).  We then create a Spritesheet passing in the image, as well as the parameters that describe the sheet.  Draw and touched aren’t called automatically, so each time we get a draw or touched call in our app, we simply call our sprite’s draw and touched functions.

 

When you run the code you should see ( assuming you used the same image I did that is! ):

IMG 0275

 

If you are curious how I got an animated gif of a playing application out of Codea and onto this site, there is a neat bit of functionality built into Codea.  You can record a video of your game running in the run screen of Codea using this button:

IMG 0282

 

As you may be able to guess, I then used the iPad app PicPlayPost to convert to animated gif.  It did a solid job and produced a small file, but it also stripped away all of the colour!  So instead here it is exported as MP4 and created with my ole trust Gif Brewery:

AnimatedBird

 

So that’s a simple project in Codea.  Next up I’m going to start looking at Vector graphics instead of sprites.

Programming


15. October 2014

 

With this post we begin our voyage of creating a game from scratch entirely on an iPad.  We start by covering what is perhaps the most important piece, Codea.  This is the part of the puzzle that actually allows us to write and run code on the iPad, so yeah… it’s a bit critical.

 

This post is a bit of a milestone of sorts, this is the very first time I’ve done a voiced over video.  I intend to start doing video production a bit more often to supplement text tutorials, articles and reviews, so I hope you don’t find my delivery overly annoying!  Bear with me a bit while I get used to doing voice work and work out the kinks on video production.  Things will get better!  Um, I hope.

 

Here is the video, don’t worry, a text and picture based version follows for those with a video aversion.  The video is recorded in 1080p, so for the best video quality, I recommend directly opening the file on YouTube.

 

 

So, what exactly is Codea?

 

Basically it’s a complete game development system for iPad, that enables you to create games for the iPad, on the iPad.  It’s Lua based, includes a complete game library, code editor, asset manager, shader programmer, tons of examples and more.  It’s available on the App Store for $10.  Let’s take a quick guided tour of Codea.

 

The Main Interface

 

The interface itself is quite clean and basic.  When you load up Codea, you are greeted by this page:

 

Photo 2014-10-15, 2 21 13 PM

 

Tools

 

Across the top are the examples, across the bottom are your projects.  You can easily clone an example into a project, as you can also duplicate existing projects.  If you notice at the top left there is a bar with an arrow to it’s right.  This brings out the tools panel:

 

Photo 2014-10-15, 2 21 23 PM

 

From the top to bottom we have:

  • Reference – Brings up the integrated help files
  • Shader Lab – An interactive (and excellent) GLSL shader editor
  • Assets -  Asset management ( graphics, sounds, shaders, etc )
  • AirCode – Enable Codea for editing using a computer with a web browser.

 

Settings

 

To the top right is the settings panel.

Photo 2014-10-15, 2 21 18 PM

 

Here you can set your theme and font sizing, and most importantly, link your Dropbox.  This allows you to Dropbox account to Codea, enabling you to easily get assets in and out of Codea, something not easily done on iOS devices.

 

Code Editing

 

When you open a project, this is where you end up:

Photo 2014-10-15, 2 22 11 PM

 

Across the top are tabs representing the files in your project.  Press the + on the top right to create a new class or file.  As you can see, across the top of the soft keyboard, they’ve added a number of keys to make programming easier.  These include a cursor for moving left and right, tab for, um… tabbing.  There is also an integrated Find and Help buttons.  If you hook up a USB keyboard, the soft keyboard goes away.

 

Here is the find dialog:

 

Photo 2014-10-15, 2 20 34 PM

 

And much more important, the integrated help files:

Photo 2014-10-15, 2 20 53 PM

 

Running your code ( clicking the Play icon at the bottom right ), brings you to this screen:

Photo 2014-10-15, 2 20 16 PM

 

The parameters section enable you to create handy UI controls to interact with your app.  Output is basically a text console.  The icons enable you to pause, restart, take a snapshot and video cap of your app running.  When you run Aircode, your iPad goes to this screen, allowing you to live edit code and see it run in real time.

 

Code Editing Helpers

 

I’ll admit, coding on a touch screen without a keyboard isn’t a great experience, but Codea have made the process somewhat more bearable.  First was the keyboard extensions above.  Additionally they have implemented some nice touch friendly features, like:

 

Color Picker:

Photo 2014-10-15, 2 22 20 PM

 

Asset Chooser:

Photo 2014-10-15, 2 22 00 PM

Codea actually includes a fair bit of free content to get you started.

 

Font Chooser:

Photo 2014-10-15, 2 22 14 PM

 

GLSL Editor

I mentioned earlier that there is an integrated GLSL editor:

Photo 2014-10-15, 2 21 33 PM

You can edit the Vertex and Fragment scripts and see the result in realtime on the right.  The Bindings is where you can set parameters in to your shader.  As I said in the video, I really hope this gets spun off into it’s own product!

 

 

It’s a clean, simple but fairly comprehensive set of tools.  The included samples are quite impressive as well.   We will cover coding at a point in the future.

 

So that’s Codea.  We will get to know it a lot better of the next few weeks.

Programming


27. March 2014

 

Wow, I gotta say this announcement pretty much broadsided me.  I wrote Moai off for dead, which was a huge shame, as I did a tutorial series on the subject, and frankly found MOAI to be some of the cleanest C++ code I had ever read.  So to hear of a new release certainly makes me happy.

image

So then, what’s new?

 

Feature merge from Zipline:

  • Massive rework of the garbage collection system to simplify object tracking
  • Reworking of the AKU Modules API
  • New SDL host to complement the GLUT host
  • A flag to MOAIAction to disable auto-stop on no children

A New Build System:

  • The CMake build system that was in place for the linux and blackberry hosts was extened to include all hosts
  • Luajit support was added to all platforms (via cmake scripts)
  • create-project-* scripts were created that allows you to generate vs2008->vs2013 and xcode project files.
  • Changes to a project can be recorded once in the relevant cmake script and all platforms should work instead of maintaining 6+ build configs
  • Plugin support was added for installing modules which are outside the moai sdk (other repos) [More]
  • Custom host support was added for compiling a host outside of the repo [More]
  • Support for compiling with Ming-w64 for a completely open source experience on Windows (no more vcruntime to distribute)

Additional Features:

  • Vector Pathfinding (MOAIVecPathGraph)
  • Box2D Closest Raycast Exposure
  • Twitter support in android
  • MOAIShader was enhanced with the 4x4 matrix and given access to UNIFORM_WORLD_VIEW
  • A faster and smoother android host with working keyboard support.
  • MOAISafariIOS is now MOAIBrowserIOS and MOAIBrowserAndroid added
  • Visibility can now be inherited!
  • Google Play Services
  • Updated Tapjoy and Vungle
  • MOAISim showCursor and hideCursor support
  • Documentation improvements
  • Chartboost for IOS and android
  • You can now EnqueueJoystickEvent from the host.
  • New HTML/JS host based on emscripten

Fixes:

  • Android logging support repaired
  • VFS works correctly on all platforms
  • A heap of lua stack overflows
  • Fixes to JSON parser and encoder
  • Textbox string bounds calculations fixed
  • PVR support working now

 

Certainly some nice new features in there, and hopefully this new release will breath renewed life into this project that frankly always deserved a better fate.

 

You can access the MOAI source code here.

You can read a more complete release notes here.

News Programming


AppGameKit Studio

See More Tutorials on DevGa.me!

Month List