366days since
Charity Parachute Jump!

Amount Raised

So far the grand total raised
for my Charity Jump is

£570!!!

+ $0 USD via Paypal

DONE IT!

I've set a total of £500 as a minimum value to raise, let's see how far I can push the limit!

UPDATE : (July 2011) - I did a prototype of this game in Flash a while         back as a learning exercise and I've recently begun work on an iPad / Android tablet port of the flash version - expect updates and some news / screen shots soon!

 (alternatively check out the development blog below and follow the game's progress as it unfolds).




Jack, a plumber by trade, a more dedicated waste water warrior you could never hope to find (especially with callout charges being what they are these days!), his pipe fitting prowess is legendary, his skills with the spanner, outstanding!· But lately Jack's work has been slipping (or should that be dripping...) and only YOU can stop Jack from becoming "washed up" and his career from going "down the drain"!
 
  WhiteTree Games - first published project is a polished original take on a classic PipePacked puzzler, follow Jack through his dreams and help turn his Plumbing nightmares in to Perfect PipeDreams, with several hundred different hand crafted levels, differing levels of complexity and a library of inspirational quotes to help keep you centred, PipeDreams offers plenty of replay value and lot's of hidden treats to find.
 

This isn't the PipePuzzling game you might think it is - if you fancy a fresh take on a old idea, then why not take the challenge now!

With over 800 plumbing nightmares to sort out, can you rearrange the pipes and close off all the loose ends before the timer runs out?

Or do you fancy the opportunity to sit back and relax, listening to your favourite music, as you take your time solving endless puzzle after puzzle, whilst being offered little snippets of wisdom to give you something to ponder on...

 
 
        Available at the special bargain sale price of $1.99 (final cost in other currencies may vary),
            click the button below to order the game using our secure server.

(note this is the original PC version - requires either Win 98 / XP / Vista / 7)
 
 
 

* No Plumbers were hurt in the making of this game!


The best 59p I ever spent...

posted 21 Jun 2011 12:27 by Jon Howard   [ updated 21 Jun 2011 13:21 ]

So following on from last weeks post I thought I'd take the time to chat a little more about the SDK / development environment I'm using for this project.

The Corona SDK (www.anscamobile.com) is the brain child of some ex-adobe employee's who apparently got frustrated whilst working on the Flash-Lite (the original port of Flash to mobile devices) and decided that there must be a better way.

Well it turns out that there is and it's a combination of their mid level graphics api, a selection of core native OS functionality and the latest (v5.1) Lua interpreter.

Even before Apple relaxed the rules regarding what languages app's could be written in, lua was a game dev's favorite as it's implemented within your app as a vanilla C library and allowed for a fast development cycle and was an ideal "high level" addon to provide generic  scripting within the games exposed framework.

This is where we come back to Corona - the ansca guys have cleverly abstracted away all of the low level OpenGL malarky and all of the  native stuff required to actually get a basic app working on a mobile device and you just supply a simple script (main.lua) and away you go (actually it's a bit more complex than that if you want to do the job properly - but the ideas sound).

It's not a new idea either - there's at least one homebrew dev'ing environment on the PSP that uses a similar approach and it actually works out quite well - that is if you know how to program in lua!

Actually lua IS a fairly simple language to learn BUT it does come with major pitfalls that can trip up the unwary or inexperienced.

For one, it's not natively an OOP language (that's object orientated) however you CAN (and IMHO SHOULD) implement a kind of class / object structure on your code using the multipurpose built in table data structure.

Because of it's free form nature lua code CAN very easily get very messy with bit's of code mixed in and around local function definitions, referencing stuff here there and everywhere and I can tell you from personal exeperience that trying to manage / maintain code like that is a nightmare (even more if the original author is no longer present!) - the only way to get round that is to be incredibly self-disclplined and enforce a rigid code structure and be prepared to reorganise and potentially refactor code as and when required or else you'll very quickly be on the wrong end of a slipperly slide into a pile of spaghetti code!

Lastly (and my personal biggest bugbear with lua and languages of it's ilk) is the fact that it's loosely typed - what this means is basically you declare variables without any regard to type and let the language sort out what it thinks you meant based on the data your passing at a given point, it also means that you can simply create variables as and when needed and if the compiler / interpreter doesn't recognise the variable it simply assumes it's a new one and creates it there and then - whilst this might seem like a good idea, anyone with any experience in a tightly typed language (like C or even tighter C++) will instantly know at the compile time when they mis-spelled a variable name (as the compiler will complain bitterly at the point it happens) not several functions later when your trying to debug at run time why a variable has got the wrong value or wasn't updated when you expected it to be.

But as I said - that's just my humble opinion AND despite all of that I'm actually enjoying the learing of a new language (and one which could actually be of a decent commericial benefit in the future).

So what's this 59p thing about then?

I'm getting to that - actually one of the nicest things about the Corona SDK is how open, friendly and helpful the development community is (this is INCREDIBLY helpful when starting out) and I've noticed that there seems to be no limit to how far some of the Corona forumites will go to help out fellow dev'ers in need.

There are a couple of developers in particular Jonathan Beebe and Ricardo Rauber who've been very helpful to the community by openly sharing some really indepth and complete code examples - Jonathon's open source Martian Control (flight control) and Ghosts vs Monsters (angry birds) remakes are a great resource to learn exactly how a Corona app should be structured (even if they could do with a little more commenting on occasion 8-)) - but the real jewel in the crown is Ricardo's director (and recently released) loader source files.

Remember I talked about how there are many ways to layer OOP concepts into lua - well Ricardo has (IMHO) demonstrated two of the best with his high level "director.lua" and lower level "loader.lua" files.

When first trying to get my head around Lua I posted my own attempt at utilising a table to provide an object instance and posted it to the corona forum (link) and it was faily well recieved, however shortly after that Ricardo released the latest (v1.3) of his director.lua file and the initial release of his loader.lua file.  

On their own it can be difficult to see how exactly they should be used, Jon Beebe (in common with most of the more complex Corona examples) - uses director.lua as a screen management class so it controls the transition between screens / levels etc, something which it's brilliant for - and if a successful indie app author with 100's of thousands of downloads is using a system like that it can't be all bad - right?

And it's not - director is simple in it's execution, enforces a clean strucure (each "directed" lua screen consists of a single function that returns a corona display group and manages the entire screen as a single item - whoo hoo - oop in a can!) - however the code inside each "directed" screen can still get a little messy as it's basically a complete mini lua app in it's own right and is subject to all the potential issues mentioned above.

That's where loader comes in - Ricardo actually released a full minesweeper clone called dev-mine (available on the app store for free) that utilises the loader class extensively.  In (what I thought was an inspired and very (if not overly) generous move) Ricardo actually offers the full source to DevMine available to anyone for the price of a single in app purchase of 59p ($0.99)

Disecting the source to DevMine proved to be a lightbulb moment for me and the source is what I'd call a master class in corona app development, covering ALL aspects of the SDK from input, animation, event handling, through to in app purchases and core data storeage through sqlite! - so much so that I would go so far as to say that it's actually worth well over 100x the cost I paid for it because without the loader class PipeDreams wouldn't be progressing at the rapid rate it is and the code wouldn't be anywhere near as a clean and as well structured as it is.

Like Director Loader wraps up a single lua file and treats it like an object but it greatly simplifies the code interface and can provide the full range of OOP requirements (encapsulation, inheritence and polymorphism)

You don't actually need to use Director with Loader, DevMine doesn't, and because of the fact that I'm actually porting my existing Flash / Haxe version of PipeDreams which already uses it's own transitioning logic I'm not going to either. However if I was starting from scratch I'd probably choose to use a combination of Director for the main screen transitions / management and Loader for the actual object management / resource loading.

To demonstrate what I mean with regards to Loader (and to illustrate several proper OOP concepts in lua), here's an example bit of code from the current PipeDreams demo - it manages a single instance of my background class which is a combination of a static image and up to three layers of parallax scrolling clouds.

(edit : hmm - not sure what's going on with the source, when I view it in the editor it's tabbed correctly, however they seem to disappear when viewed normally 8-( - please bear with me whilst I try and sort it out!)
--<code - snip this as background.lua>

module(..., package.seeall)

--[[ 
Background.lua
******************
- DONE - all complete!
- TODO
--]]

-- ------------------------------------------------------------
-- Static (private) data for this class... 

-- because this is "local" to this module it's effectively hidden from everything else!
--
-- You can implement class variables and methods here by editing these values or calling these 
-- functions from within the actual object instance!

local mRand = math.random

local scrWidth  = display.contentWidth
local scrHeight = display.contentHeight

local function choose ( array ) local index = mRand(1,#array) return array[index] end

-- ------------------------------------------------------------
-- Normal cloud transition speeds

local backSpeeds = { 0.35,-0.35,0.45,0,-0.45,0.55,-0.55,0 }
local midSpeeds  = { 0.4,-0.4,0.55,-0.55,0.65,-0.65,0.7,-0.7 }
local foreSpeeds = { 0.7,0.8,0.85,0.9,0.9,0.95,1.0,1.25,-0.7,-0.8,-0.85,-0.95,-1.0,-1.25 }

local backImgs   = {3, 5, 6, 8, 9, 11, 12, 14, 15}
local midImgs    = {2,3,4,7,10,13}
local foreImgs   = {0,1,4,6,11,12}

-- Title screen cloud transition speeds

local titleSpeeds = { 0.55, -0.55, 1 }

-- Size / speed scaler from 640x480 to 1024x768

local scaler = 1.6

-- ------------------------------------------------------------
-- ------------------------------------------------------------
-- ------------------------------------------------------------
-- Class body - the entire class (object instance) is wrapped up inside this function.
-- "new" is called from within "loader.lua" with the supplied paremters represented by the "params" table

function new ( params )

--[[
Create a single instance of a background with three layers of parallax scrolling clouds.
--]]

-- Useful check to make sure no memory / resources are being leaked - it isn't! 8-)
-- print("Memory at start = " .. collectgarbage("count"))
-- print("NEW : Background (type " .. params.type .. ")")


----------------------
-- Object instance data (and defaults)
-- Only add in here the minimum amount required to implement the object's functionality
-- everything else should be done as a class variable (see above!)
----------------------
local obj = display.newGroup() -- the single compound data object! - this is what get's returned 

local active = false
local clouds = { }
local state = 0
local myParent = nil
-- --------------------------------------------------------------------------------------
-- Local functions are effectively private as there is no access to them.
-- Public functions have to be added to the display group that's returned.
-- --------------------------------------------------------------------------------------

----------------------
-- Construct the actual object (put it in a function so I can use some function level locals rather than adding
--                              extra unnecessary data to the object instance)
local function construct ( par ) -- all parameters to the object are passed via a single table!
local auto = true
local back = display.newImage(par.img)
local cimg,speed,width,img
obj:insert(back)
myParent = par.parent
if par.type == 1 then -- title clouds
for i=1,3 do
cimg = display.newImage(par.clouds[i])
cimg:setReferencePoint(display.TopLeftReferencePoint)
cimg.x = 0
cimg.y = 0
cimg.xScale = scaler
cimg.yScale = scaler
speed = titleSpeeds[i] * scaler
width = cimg.width * scaler
-- print("Cloud " .. i .. " width = " .. width)
clouds[i] = { cimg, speed, width }
obj:insert(cimg)
end
else -- normal clouds
-- background layer --------------------------------------
for i=1,3 do
img = choose(backImgs)

cimg = display.newImage(par.clouds[1+img])
cimg:setReferencePoint(display.TopLeftReferencePoint)
cimg.x = mRand(0,scrWidth)
cimg.y = mRand(0,240) - 88
cimg.xScale = scaler
cimg.yScale = scaler
speed = (choose(backSpeeds) / 1.2) * scaler
width = cimg.width * scaler
-- print("BCloud " .. i .. " ["..img.."] width = " .. width.. " x = " .. cimg.x .. " speed = ".. speed)
clouds[i] = { cimg, speed, width }
obj:insert(cimg)
end
-- midground layer --------------------------------------
for i=1,3 do

img = choose(midImgs)

cimg = display.newImage(par.clouds[1+img])
cimg:setReferencePoint(display.TopLeftReferencePoint)
cimg.x = mRand(0,scrWidth)
cimg.y = mRand(0,240) - 88
cimg.xScale = scaler
cimg.yScale = scaler
speed = (choose(midSpeeds) / 1.5) * scaler
width = cimg.width * scaler
-- print("MCloud " .. i .. " ["..img.."] width = " .. width.. " x = " .. cimg.x .. " speed = ".. speed)
clouds[i] = { cimg, speed, width }
obj:insert(cimg)
end
-- foreground layer --------------------------------------
for i=1,3 do
img = choose(foreImgs)

cimg = display.newImage(par.clouds[1+img])
cimg:setReferencePoint(display.TopLeftReferencePoint)
cimg.x = mRand(0,scrWidth)
cimg.y = mRand(0,240) - 88
cimg.xScale = scaler
cimg.yScale = scaler
speed = (choose(foreSpeeds) / 1.25) * scaler
width = cimg.width * scaler
-- print("FCloud " .. i .. " ["..img.."] width = " .. width.. " x = " .. cimg.x .. " speed = ".. speed)
clouds[i] = { cimg, speed, width }
obj:insert(cimg)
end
end

---------------------------------------------------
-- Start the update cycle...

-- print("Clouds start")
Runtime:addEventListener( "enterFrame", obj )
active = true
---------------------------------------------------
-- transition the alpha bit in...
obj.alpha = 0; transition.to(obj,{time=1000,alpha=1})

end

-- --------------------------------------------------------------------------------------
-- Class implementation...
--
-- These functions form the "class interface" and are publicly visible to the outside world!

----------------------
-- Update function for this object
----------------------

function obj:enterFrame(event)
--print("Update the clouds")
local x,s,w,img

for i=1,#clouds do
img = clouds[i][1]; s = clouds[i][2]; w = clouds[i][3]
x = img.x + s
if s < 0 then 
if x < (0-w) then x = x + (scrWidth + w) end
else
if x > scrWidth then x = 0 - w end
end
img.x = x
end
end

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

function obj:destroy()
-- print("Remove clouds")
if active == true then
-- print("Clouds stop")
Runtime:removeEventListener( "enterFrame", obj )
active = false
end
obj:removeSelf() -- loose the display object references
obj.active = nil -- and all the extra references we added
obj.state = nil
obj.clouds = nil
obj = nil -- finally kill the object reference itself
-- collectgarbage("collect"); -- make sure this is called periodically somewhere in the app 
-- print ("Memory at end = " .. collectgarbage("count"))
end
-- --------------------------------------------------------------------------------------
function obj:fadeOut()
-- Trigger a fade out, then on complete call the remove function
-- print("Clouds fade out");
local function fadeDone(event) obj:destroy() end
transition.to(obj,{time=1000,alpha=0,onComplete=fadeDone})
end
-- --------------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------------
-- Class construction (now that everything has been created / defined etc)...
construct( params ) -- call the constructor
myParent:insert(obj) -- add the display object directly to it's parent group (optional)

return obj -- return a reference to your new "object"!
end

-- End of file... 

--</code>

The individual backgrounds are then all managed through a single background controller object like this...

--<code - snip this file as backctrl.lua>

module(..., package.seeall)

--[[ 

Backctrl.lua
******************
- DONE
. Complete
- TODO

--]]

local mRand = math.random

-- ------------------------------------------------------------
-- Static data for this class...

local backgroundImages = { 
"images/title_back.png",
"images/01-Back.png", "images/02-Back.png", "images/03-Back.png",
"images/04-Back.png", "images/05-Back.png", "images/06-Back.png",
"images/07-Back.png",
}

local titleClouds = { "images/Cloud-Layer-3.png", "images/Cloud-Layer-2.png", "images/Cloud-Layer-1.png" }

local cloudImages = { 

"images/layer1.png", "images/layer2.png", "images/layer3.png", "images/layer4.png",
"images/layer5.png", "images/layer6.png", "images/layer7.png", "images/layer8.png",
"images/layer9.png", "images/layer10.png","images/layer11.png","images/layer12.png",
"images/layer13.png","images/layer14.png","images/layer15.png","images/layer16.png",
}


-- ------------------------------------------------------------
-- ------------------------------------------------------------
-- ------------------------------------------------------------
-- Class body

function new ( params )

--[[
Create a single instance of a background controller object that is responsible for loading all of the background
images and cloud layers in.
It also creates an instance of the first background (which then updates itself including fading in / out).
The controller then sits dormant until it's signalled to change the background - which it does by telling the currently
active background to fade out (and automagically delete itself) and then creating a new current background - which again
goes off and updates itself (including the fading in / out etc).
I've tested it exhaustively using the values returned from the collectgarbage() function to make sure it doesn't leak
any memory and all references etc get cleaned up properly - and it seems to work flawlessly.
Obviously there is no quit / closedown functionality for the backgroundController as it never quits until forcibly shut
down along with the rest of the app by the OS.
--]]

print("NEW : BackgroundController")

----------------------
-- Object data (and defaults)
----------------------
local obj = display.newGroup() -- the compound data object!
local myParent = params.parent

local currScreen, nextScreen
local active = false
local current = 0
-- --------------------------------------------------------------------------------------
-- Local functions are private as there is no access to them.
-- Public functions have to be added to the display group that's returned.
-- --------------------------------------------------------------------------------------
----------------------
-- Construct the actual object (put it in a function so I can use some locals)...
local function construct ( par )
-- Override the defaults if the parameters table was supplied
myParent = par.parent
-- print("myParent "..type(myParent))
-- Pre load in all the graphic elements - this "hack" ensures all the graphics are ready when we need
-- them and will prevent any "stutter" later on.
local name

for i=1,#backgroundImages do name = backgroundImages[i]; loader.loadImage(name) end
for i=1,#titleClouds      do name = titleClouds[i];      loader.loadImage(name) end 
for i=1,#cloudImages      do name = cloudImages[i];      loader.loadImage(name) end

currScreen = nil
end

-- --------------------------------------------------------------------------------------
-- Class interface / implementation...

----------------------
-- Change the background type
----------------------

function obj:change(index)
-- print("Change background to type ".. index)
if index == current then return end -- ignore requests to change the background to the same as the existing one!
if currScreen ~= nil then currScreen:fadeOut() end -- tell the current background to shut itself down
local ref
if index == 1 then ref = titleClouds else ref = cloudImages end

-- Create a new "current" background of the correct type (fading in etc is all handled by the background class!
currScreen = loader.newGroup("background",{parent=myParent,img=backgroundImages[index], clouds=ref, type=index})
current = index
end

-- --------------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------------
-- --------------------------------------------------------------------------------------
-- Class construction (now that everything has been created)...
-- print("BackCtrl.params.parent = " .. type(params.parent))
construct( params )
if myparent ~= nil then myparent:insert(obj) end -- Auto add the controller to the specified parent display group!
obj:change(1) -- Initially make the title screen active!
return obj
end

-- End of file... 

--</code>

And lastly here's the code to create the main background controller - due to the preloading of the images this is done towards the start
of main.lua so that the preloading is hidden by the display of Default.png

The main background controller is implemented as a global singleton so I can signal a smooth alpha cross fade background change from 
anywhere in the app.

--<code>

grpBackground = display.newGroup() -- Create a single display group to hold all the backgrounds

objBackCtrl = loader.newGroup("backctrl",{parent=grpBackground}) -- Create the background controller (fades in the title screen by default)...

...
...
...

objBackCtrl:change(newBackground) -- Kick of a transition to another background - all the cleanup etc is handled internally!

...
...


--</code>

As soon as I get some kind of screen recording system in place I'll post a video on YouTube and you'll be able to see the background's 
in motion controlled by this code!

Both Director and Loader are available via the code exchange on the Corona forum and directly from RauberLabs, but if your serious about 
using Corona to speed up your mobile game development my advice would be to download "Dev Mine" and cough up the 59p for the full source 
code.

It'll be the best 59p you'll ever spend!

Oh - one last tip.

Interally loader.lua uses the pcall() function to load the module and execute the functions, but as pcall effectively traps any exceptions that get generated at runtime this means that you won't see the simulator display any useful error messages when you make a mistake (it'll just cause pcall() to return nil and get loader to display a generic message) so I just replaced / removed the pcall() statements in "callFunction" and "loadModule" it still works perfectly and it makes life a LOT easier to see where you've gone wrong! 

Sorry if this was an overly long post *sheepish grin*

Till next time...

Development Blog #1

posted 16 Jun 2011 05:16 by Jon Howard

As you may have noticed from the main page - I've begun work on an IOS / Android port of the game.  It won't be an exact port as a while back I spent some time creating a version of the game in Flash (or Haxe to be precise) and during that time I spent a lot of time playing and replaying the game trying to find ways to make the game more appealing to the causal 5 minute play brigade and to incorporate a greater sense of progression through the game through the use of a challenge structure, achievements and new modes to unlock (I'll post more details on the new / improved game design later).

Actually this is the second time I've started an IOS port - I actually had a version up and running on my iPod touch last year using a the DragonFireSDK Windows iPhone development system - this version actually stalled for a number of reasons, one of the main ones being that trying to shoe-horn the 640x480 display of the Windows / Flash version into the (landscape) 480x320 iPhone res meant that the graphics / touch areas were too small to be realistically playable (Apple recommends a minimum size of 44 pixels square).

However throwing the iPad into the equation gives us a completely different ballgame - in fact I've had to scale up (and in some cases re-touch) the original artwork to fit the iPad's 1024x768 landscape res (the tech-heads amongst you will notice that the iPad has the same aspect ratio as SVGA and increasing everything by a standard scale of 1.6x will make all the layouts etc easy to handle).

So what about Android?  Well the nice thing here is that I'm going to be using the Corona SDK instead for the development (Corona is a native graphics engine for iOS that is controlled through a tightly integrated Lua scripting engine).  Because you can only access the Corona API through Lua app's can be natively recompiled for Android with little (and in many cases no) reworking - so the Android version effectively comes for free! BONUS!

Anyway that's enough for a first post - expect to see some period updates as development work progresses (other commitments mean this has effectively been reduced to a part time project - but that's OK as coding is a big hobby of mine anyway and it keeps me out from under the Wife's feet) - plus as I've already got all the assets and the design sorted, it actually allows me to concentrate on the learning of Corona / Lua as a new development environment.

Till next time...

1-2 of 2