Friday 30 December 2011 1 comments

Time for reflection

Since I started this project back in October there have certainly been some challenges both related to this and those of my day to day life.

However, as I explained in my initial project kick off there is a goal to all this and setting myself a target of at least 3 hours a day for 6 days a week has certainly given me the impetus to ensure this keeps ticking along.

The more I get involved with Corona SDK by Ansca the more I feel I have made the right decision. Of course I would say that but the more I dig, the support of the community and the 3rd party applications has made this a voyage of discovery.

What I haven't mentioned to date is that I am a reasonably experienced developer having spent some 15 years within the IT industry but probably the last 8 or so have been in management so getting back to my first love of coding and being creative while solving problems is my expertise and this has certainly set me up well on this project.

Reading the game design book The Art of Game Design: A book of lenses was again a better decision than I knew at the time. This taught me to be iterative and thoughtful and not get carried away by shiny new toys as well as consider my audience / customers. That coupled with my over ambition to achieve the next AAA game or Angry Birds rival on first attempt has seen me move from game idea to a simpler game idea a few times to one I am happy to proceed with.

The real proof of this came when I handed my wife the first alpha release of my game (copious amounts of bugs) of which she continually played it over and over for some 20 minutes. The feeling of satisfaction to not only get to grips with the Corona SDK to actually produce something that worked but so a completed non gamer (casual gamer if you like) and interact in how you intended with little instructions is the motivator for the next 200 hours, 1000 hours, hell why not the whole 10000.

The turning point was certainly this week. I was struggling. I had spent an inordinate amount of time getting pretty much no where. I had been using the Corona SDK trial version which is some builds behind the indie license and of course the daily builds. I upgraded to the stable paid version and this was when the light bulb moment happened.

I had been struggling with device performance issues. Like the XCode simulator the Corona one doesn't bottleneck performance relative to a device but instead is happy to consume most of my Mac Book Pro's power to make me think all was well. Compiling to the device pretty much rendered it useless.

I had been using the Director Class by Ricardo Rauber to control scenes while within the trial and while excellent Ansca had released their own supported version called Storyboard. Once I had paid for the indie license the layout of Storyboard just seemed to open my eyes up to how it should all hang together. As a result I have completed rewritten my game with 48 hours, achieving 55 to 60 fps and half way to integrating open feint into the game.

I also found an excellent tool called Physics Editor by Code 'n' Web which has greatly improved my collision detection mapping. A tool that only costs $20, allows you to import your images and then hit auto trace. Output to Corona and you have the physics data you need to provide accurate collision mapping. This alone has saved me hours but given a far greater polish to the experience.

So in summary feeling pretty confident about getting the game out into the app store and android market and the benefit that Corona SDK gives me is that I can target iPad, iPhone, iPod, Android devices, Nook and Amazon Fire with very little to worry about. Also with a greatly improved advertising network which on first sight looks far better than iAd (click rates to be verified) a multi device app should be available early 2012.

And this is good as it brings me back on schedule for releasing 3 games within the first year as part of my project.

It should be a happy new year and I wish you the same
Monday 19 December 2011 0 comments

Catmull Splines: Object follows path

Following on from my introductory blog on catmull splines I have applied and worked on an example simplifying the excellent Flight Path sample template which can be found here for the Corona SDK framework.

What I was trying to do is understand the equation I put forward an started making it a little more useful to mine (and hopefully your needs)

Right lets look at some code. Only two files here and have tried commenting as much as possible

main.lua
-- This line makes the physics engine features available under the “physics” namespace
require( "physics" )
-- A further file to create a smooth curve when drawing
require( "smoothCurve")

-- ither instantiates or resumes the world. IMPORTANT: it must be called before any of the other functions in this section.
physics.start()
-- ets the x,y components of the global gravity vector, in units of m/s2. The default is ( 0, 9.8 ) to simulate standard
-- Earth gravity, pointing downwards on the y-axis.
physics.setGravity(0,0) 

-- Creates a group in which you can add and remove child display objects. Initially, there are no children in a group. 
-- The local origin is at the parent’s origin; the reference point is initialized to this local origin
local mainGroup = display.newGroup()
local lineGroup = display.newGroup()  mainGroup:insert(lineGroup)
local objectGroup = display.newGroup()  mainGroup:insert(objectGroup)

-- Calculation interval of drawing between the dotted lines
local lineDrawInterval = 50

-- Method called when our object (blue box) is touched
function onObjectTouched(self, event)
--[[
  Touch events are a special kind of hit event. When a user's finger touches the screen, they are starting a sequence of touch events, each with different phases.

  event.name is the string "touch".
  event.x is the x-position in screen coordinates of the touch.
  event.y is the y-position in screen coordinates of the touch.
  event.xStart is the x-position of the touch from the "began" phase of the touch sequence.
  event.yStart is the y-position of the touch from the "began" phase of the touch sequence.
  event.phase is a string identifying where in the touch sequence the event occurred:
   "began" a finger touched the screen.
   "moved" a finger moved on the screen.
   "ended" a finger was lifted from the screen.
   "cancelled" the system cancelled tracking of the touch.

]]
 if(event.phase == "began") then
  -- Determine whether we are to draw a line
  self.allowLineDraw = true
  -- Determine the stype of line output
  self.lineType = "dotted"
  -- Instantiate our table to hold the points
  self.points = {}
  -- Insert our initial touch
  table.insert(self.points, {x = event.x, y = event.y})
  -- Returns a reference to the current stage object, which is the root group for all display objects and groups. 
  -- Currently, Corona has a single stage instance, so this function always returns a reference to the same object
  display.getCurrentStage():setFocus( self )
  -- Set the focus to our object (box)
  self.isFocus = true
 elseif (event.phase == "moved" and self.isFocus == true) then
  -- Make a check to see we have at least one point
  if(#self.points == 0) then
   -- Instantiate our table to hold the points
   self.points = {}
   -- Insert our current position
   table.insert(self.points, {x = self.x, y = self.y})
   table.insert(self.points, {x = event.x, y = event.y})
  end
  -- Determine the distance between our points
  -- math.sqrt: Returns the square root of a value.
  -- math.pow: Returns the result of raising a number to the power of another number.
  local distance = math.sqrt(math.pow(self.points[#self.points].x - event.x, 2) + math.pow(self.points[#self.points].y - event.y, 2))
  -- Insert our current position
  table.insert(self.points, {x = event.x, y = event.y})
  -- If we have set our object to draw a line first check
  if(self.allowLineDraw == true) then 
   -- Draw the line
   drawLine(self) 
   -- Now get our object to follow it 
   followPoints(self)   
   -- Since we have followed it and moved over our control point, remove the lone
   self.allowLineDraw = false
   -- Dependent on our line draw interval recall our method 
   timer.performWithDelay(lineDrawInterval, function(new) self.allowLineDraw = true end)
  end
 elseif(event.phase == "ended") then
  -- Now that we have lifted our touch determined the distance
  local distance = math.sqrt(math.pow(self.points[#self.points].x - event.x, 2) + math.pow(self.points[#self.points].y - event.y, 2))
  -- Returns a reference to the current stage object, which is the root group for all display objects and groups. 
  -- Currently, Corona has a single stage instance, so this function always returns a reference to the same object
  display.getCurrentStage():setFocus(nil)
  -- Remove the focus from our object now
  self.isFocus = false
  -- Follow to the end point
  followPoints(self)  
  -- Show on the stage the line to be solid
  self.lineType = "solid"
  -- Draw it solid rather than dotted
  drawLine(self)
 end
end

function drawLine(box)
 -- Do we have a line already
 if(box.lineGroup ~= nil) then
  -- Make sure we destory anything in memory 
  box.lineGroup:removeSelf()
  box.lineGroup = nil 
 end
 -- Retrieve our point (see other description) i.e. our catmull spline
 local smoothPoints = smoothCurve.getSmoothCurvePoints(box.points)
 -- If we don't have any points then exit now 
 if(smoothPoints == nil) then return end
 
 -- Lets create a new linegroup display object for our box 
 box.lineGroup = display.newGroup()
 --insert this into our linegroup display object on our stage 
 lineGroup:insert(box.lineGroup)

 -- Return modulus dependent on line type
 local modNumber = box.lineType == "dotted" and 2 or 1
 
 -- Iterate through our points
 for i = 0 ,#smoothPoints do
  if(i % modNumber == 0 and smoothPoints[i] ~= nil and smoothPoints[i + 1] ~= nil) then 
   local line = display.newLine(smoothPoints[i].x, smoothPoints[i].y, smoothPoints[i + 1].x, smoothPoints[i + 1].y)
   
   -- Determine line width dependent on modulus
   line.width = modNumber == 1 and 1 or 3
   -- Insert the line into our display object
   box.lineGroup:insert(line)
  end
 end
 -- Return the points now we have them all
 return smoothPoints
 
end 

function followPoints(box)
 -- Check to see if we have any points if not exit
 if(box.points == nil or #box.points == 0) then 
  return 
 end
 -- Get our point to follow
 point = box.points[1]
 -- Get the angle for our box to move
 local angle = math.atan2((box.y - point.y) , (box.x - point.x) ) * (180 / math.pi)
 -- Detemine the x and y velocity to move our object
 local velocityX = math.cos(math.rad(angle)) * 50 * -1
  local velocityY = math.sin(math.rad(angle)) * 50 * -1
 -- Now set the object (box) velocity
 box:setLinearVelocity( velocityX, velocityY)
 -- Initialise our method
 local enterFrameFunction = nil
 local checkForNextPoint
 checkForNextPoint = function(box)
  -- Do we have an object (box)
  if(box ~= nil) then
   -- Get our destination
   local dest = box.dest
   -- Return the velocity we have just set
   local velX, velY = box:getLinearVelocity()
   -- Cbeck a load of stuff
   if( (velX < 0 and box.x < dest.x and velY < 0 and box.y < dest.y) or  (velX > 0 and box.x > dest.x and velY < 0 and box.y < dest.y) or
    (velX > 0 and box.x > dest.x and velY > 0 and box.y > dest.y) or  (velX < 0 and box.x < dest.x and velY > 0 and box.y > dest.y) or #box.points == 0) then
    -- Remove our event listener
    Runtime:removeEventListener("enterFrame", enterFrameFunction)
    -- Remove the point as we have just got to it so not to keep drawing the same one over and over
    table.remove(box.points, 1)
    -- Now draw it
    drawLine(box)
    -- If we have at least another point left then repeat all this again
    if(#box.points > 0) then
     followPoints(box)
    else
     -- If we don't then stop the object (box) waiting for another touch
     box:setLinearVelocity( 0, 0)
    end

   end  
  else
   -- Nothing to do so remove our listener
   Runtime:removeEventListener("enterFrame", enterFrameFunction)
  end
 end
 
 -- Set the box destination to the current point
 box.dest = point
 -- Lets do it all again
 enterFrameFunction = function(event) checkForNextPoint(box) end
 -- Add another listener
 Runtime:addEventListener("enterFrame", enterFrameFunction)

end

-- Lets create our object which is a blue box
local box = display.newImage( "bluesquare.jpeg")
-- Position it roughly top left corner
box.x = 50
box.y = 50
-- Sets its calling method for when the object (box) is touched
box.touch = onObjectTouched
-- Add a listener so it will do something
box:addEventListener("touch", box)
-- Apply physics so that velocity etc will have some effect
physics.addBody(box, "dynamic",  {density = 100, isSensor = true, radius = 40})
-- Now add our local box to the main object display group
objectGroup:insert(box)

and the all important catmull splines magic

smoothCurve.lua
--[[
Creates a module. If there is a table in package.loaded[name], this table is the module. 
Otherwise, if there is a global table t with the given name, this table is the module. 
Otherwise creates a new table t and sets it as the value of the global name and the value of package.loaded[name]. 
This function also initializes t._NAME with the given name, t._M with the module (t itself), 
and t._PACKAGE with the package name (the full module name minus last component; see below). 
Finally, module sets t as the new environment of the current function and the new value of package.loaded[name], so that require returns t.

This function may receive optional options after the module name, where each option is a function to be applied over the module.
]]
module(..., package.seeall)

function getSmoothCurvePoints(points)

 -- Number of points must be at least 3 to calculate the catmull spline
 if(#points < 3) then return nil end
 -- Create our table to hold our points
 local smoothPoints = {}
    -- Steps Per Segment - The Higher The Number - The Smoother The Curve - The Longer It Takes To Calculate
    local curveSteps = 30
    -- First Segment (Remember the complication control points in the introduction blog)
    local firstSegement = drawCatmullSpline( points[1] , points[1] , points[2] , points[3] , curveSteps )
    -- Increment through control points and add it to our table
    for i = 1 , #firstSegement , 1 do
   table.insert(smoothPoints, {x = firstSegement[i].x, y = firstSegement[i].y})
    end
    -- Now increment through our segments inbetween
    for i = 2 , #points - 2 , 1 do
            local middleSegment = drawCatmullSpline( points[i-1] , points[i] , points[i+1] , points[i+2] , curveSteps )
            for i = 2 , #middleSegment , 1 do
               --Add our smoothpoints between our control points
      table.insert(smoothPoints, {x = middleSegment[i].x, y = middleSegment[i].y})
            end
    end
    
    -- Just to finish it all off apply our last segment
    local lastSegment = drawCatmullSpline( points[#points-2] , points[#points-1] , points[#points] , points[#points] , curveSteps )
    for i = 2 , #lastSegment , 1 do
   table.insert(smoothPoints, {x = lastSegment[i].x, y = lastSegment[i].y})
    end
 -- Now return our nice smooty curve
 return smoothPoints
end

function drawCatmullSpline( p0 , p1 , p2 , p3 , steps )
  -- Lets create a table for our points
    local points = {}
 
    for t = 0 , 1 , 1 / steps do
   -- Remember that complicated catmull spline equation in my blog.  Here we use it to work with our control points supplied
            local xPoint = 0.5 * ( ( 2 * p1.x ) + ( p2.x - p0.x ) * t + ( 2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x ) * t * t + ( 3 * p1.x - p0.x - 3 * p2.x + p3.x ) * t * t * t )
            local yPoint = 0.5 * ( ( 2 * p1.y ) + ( p2.y - p0.y ) * t + ( 2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y ) * t * t + ( 3 * p1.y - p0.y - 3 * p2.y + p3.y ) * t * t * t )
            
   -- Now insert these into the our table
            table.insert( points , { x = xPoint , y = yPoint } )
    end
    -- Finished with them all then return our points table
    return points
 
end
Right now we are cooking on gas what is this going to give us. Well here are a series of screenshots to give you a taster but why not give it a go to see what happens.

Now I have an object following a path what I want to implement is a chase and evade AI element to it. I'll post just as soon as I have worked it out.

Stay tuned ...
0 comments

Catmull Splines: An introduction

Those that had followed my project to date have known I have covered a few topics and provided commentary most notably on game design.

Having spent about three weeks on my first game concept I realised I had bitten off a little more than I could chew given my novice status in game design and development. That said which ever route I took there is a physics concept or mathematical gymnastics to perform and there was no getting away from it I was going to have to learn some stuff.

As you may also know I am currently working with the Corona SDK development framework and started looking at the excellent Flight Path sample template which can be viewed here. What I intend to do is write a mini series to document my learnings as well as hope this helps someone else in the future.

For those of you who aren't familiar with the concept of catmull splines (and don't worry I wasn't 72 hours ago), they can be described as splines which are a mathematical means of representing a curve, by specifying a series of points at intervals along the curve and defining a function that allows additional points within an interval to be calculated. There are various functions available to approximate a curve, but in this blog we will focus on a spline known as the catmull spline.

The points that define a spline are known as Control Points. One of the features of the catmull spline is that the specified curve will pass through all of the control points - this is not true of all types of splines.

To calculate a point on the curve, two points on either side of the desired point are required, as shown on the left. The point is specified by a value t that signifies the portion of the distance between the two nearest control points.

Given the control points P0, P1, P2, and P3, and the value t, the location of the point can be calculated as (assuming uniform spacing of control points):

q(t) = 0.5 *( (2 * P1) +
(-P0 + P2) * t +
(2*P0 - 5*P1 + 4*P2 - P3) * t2 +
(-P0 + 3*P1- 3*P2 + P3) * t3)

Ermmmm, don't know about you but this is gibberish to me!

While a spline segment is defined using four control points, a spline may have any number of additional control points. This results in a continuous chain of segments, each defined by the two control points that form the endpoints of the segments, plus an additional control point on either side of the endpoints. Thus for a given segment with endpoints Pn and Pn+1, the segment would be calculated using [Pn-1, Pn, Pn+1, Pn+2].

Because a segment requires control points to the outside of the segment endpoints, the segments at the extreme ends of the spline cannot be calculated. Thus, for a spline with control points 1 through N, the minimum segment that can be formulated is P1<->P2, and the maximum segment is PN-3<->PN-2. Thus, to define S segments, S+3 control points are required.

Now what does that all mean to us Corona SDK developers using lua. Well, again, if you have followed my blogs you would know I have hailed the efforts of Matthew Pringle and his Corona Remote app to aid rapid testing. Those who have used it know of the plotting of accelerometer points. Matthew very kindly posted some code on the ansca website which helps put the gibberish above into context. The code is as follows:

display.setStatusBar( display.HiddenStatusBar )
 
-- New Points Table
points = {}
 
-- New Line
local line
 
-- Add Points - Generated Randomly To Draw Jagged Line Across Screen
for i = 1, 11 , 1 do
        
        local xPoint = ( display.contentWidth / 10 ) * i - ( display.contentWidth / 10 )
        local yPoint = math.random( 50 , 250 )
        
        table.insert( points , 1 , { x = xPoint , y = yPoint } )
 
end
 
-- Draw Jagged Line
if ( #points > 2 ) then
        
        line = display.newLine( points[1].x , points[1].y , points[2].x , points[2].y )
        
        for i = 3 , #points , 1 do
        
                line:append( points[i].x , points[i].y )
        
        end 
                        
        line:setColor( 155 , 155 , 155 )
        line.width = 2
 
end
 
-- Catmull Spline Function
function drawCatmullSpline( p0 , p1 , p2 , p3 , steps )
 
        local points = {}
 
        for t = 0 , 1 , 1 / steps do
 
                local xPoint = 0.5 * ( ( 2 * p1.x ) + ( p2.x - p0.x ) * t + ( 2 * p0.x - 5 * p1.x + 4 * p2.x - p3.x ) * t * t + ( 3 * p1.x - p0.x - 3 * p2.x + p3.x ) * t * t * t )
                local yPoint = 0.5 * ( ( 2 * p1.y ) + ( p2.y - p0.y ) * t + ( 2 * p0.y - 5 * p1.y + 4 * p2.y - p3.y ) * t * t + ( 3 * p1.y - p0.y - 3 * p2.y + p3.y ) * t * t * t )
                
                table.insert( points , { x = xPoint , y = yPoint } )
                
                local dot = display.newCircle( xPoint , yPoint , 2 )
                dot:setFillColor( 255 , 0 , 0 )
 
        end
        
        return points
 
end
 
-- Draw Curve Using Catmull Spline Function
if ( #points > 2 ) then
 
        -- Steps Per Segment - The Higher The Number - The Smoother The Curve - The Longer It Takes To Calculate
        local curveSteps = 5
        
        -- First Segment
        local firstSegement = drawCatmullSpline( points[1] , points[1] , points[2] , points[3] , curveSteps )
        
        -- Draw First Segment
        local curve = display.newLine( firstSegement[1].x , firstSegement[1].y , firstSegement[2].x , firstSegement[2].y )
        for i = 3 , #firstSegement , 1 do
                curve:append( firstSegement[i].x , firstSegement[i].y )
        end
        
        -- Segments Inbetween
        for i = 2 , #points - 2 , 1 do
        
                local middleSegment = drawCatmullSpline( points[i-1] , points[i] , points[i+1] , points[i+2] , curveSteps )
                for i = 2 , #middleSegment , 1 do
                        curve:append( middleSegment[i].x , middleSegment[i].y )
                end
        
        end
        
        -- Last Segment
        local lastSegment = drawCatmullSpline( points[#points-2] , points[#points-1] , points[#points] , points[#points] , curveSteps )
        for i = 2 , #lastSegment , 1 do
                curve:append( lastSegment[i].x , lastSegment[i].y )
        end
        
        curve:setColor( 0 , 0 , 255 )
        curve.width = 2
 
end

The results of which put the equation above into something a little more tangible:


Wow that was a lot to take in but hopefully putting it all in one place has helped a little. As I mentioned I am a newbie on this so if anyone reading this thinks what the hell is he talking about this is completely wrong I am more than happy to be corrected just leave a comment.

The next in the series I will look into something more akin to a game and applying the Corona SDK template for novices. Stay tuned ...
Saturday 3 December 2011 0 comments

Game Design: Mockups

I have a little bit of artistic skill but not one that I have ever really been able to transfer to the computer, very much a pencil and paint man. I have a notebook that I put all my ideas into when I have a spark of inspiration and once I have dealt with the kids or finished my day job I can refer to.

All this is great but what I have been looking for is something that I can chuck a few transitions onto, or maybe the menu layer or the supporting website.

A tool which I have been using a lot recently is Balsamiq's mockup for wireframing some ideas. Now there are probably hundreds of tools like this but I really like the quirky wireframe designs and the inclusion of iphone templates.

What Balsamiq say about mockups:

"Mockups is zenware, meaning that it will help you get “in the zone”, and stay there. Our aim is for you to forget our software is there at all.

Mockups offers the same speed and rough feel as sketching with a pencil, but with the advantage of the digital medium: enlarging containers is just a drag operation, rearranging elements doesn’t require starting over, and your wireframes will be clear enough that you’ll be able to make sense of them tomorrow.

Mockups should make work fun."


Mockups can be bought for $79.

 
;