Saturday, March 1, 2014

Codea, initial explorations of the iPad IDE

Codea, is, an IDE for the development of multimedia apps, specifically games.  It comes complete with a few images ready for use and has a built in shader editor to prepare visual effects before use in game.  There are some cute little demos, including a simple pixel-art image editor and rudimentary drum machine.

The API itself is often times made as simple as possible.  Images are referred by their string names, and the API handles the loading part.  You just draw with it!  Touch information is stored in a global variable for easy polling.  The game loop is already set up.  And there are plenty of utility shape drawing functions.

The downside is that the API is too simple at times - I could overlook this if the app didn't claim to provide an environment enabling a person to make completely polished games for the App Store.  The simple API is great for prototyping simple interactions.

Consider the simple task of a button.  A button is ubiquitous, it should handle presses and provide appropriate feedback.  Many of the more complex examples shipped with Codea use a button, yet each has its own abstraction.  Spiritely, the pixel art tool, goes just as far as to try to replicate the native iOS feel.  But that is difficult...

Let me explain, when a button is pressed, a typical UI will make it give user feedback.  Like get darker, bevel in, etc.  If the finger wanders off the button, but not off the touchscreen, then no other button should provide feedback.  Imagine that where the finger goes down on a button, all events go back to that button.  Such a feature requires planning, and above all simply reimplementing what's been already done a million times.

Yes, there are multiple interaction paradigms.  The home screen on iOS uses the one described above. 

In the end, if I were creating just the game scene on pong, then Codea would be perfect.  If I wanted menus and more, then maybe I'd want something similar to a rudimentary GUI framework.  Which I provide below.

One last note, the tabs are executed in order.  So a super class should appear before any inheriting classes in the tabs.

In the end, I recommend Codea for prototyping on the go.  Have an idea for something?  Prototype quickly and show it to people right then and there.  No need to lug a mac to fine tune interactions in a game prototype.  It's kind of nice that way,

Below are a series of objects to use in Codea that may prove useful for UI work.  It's in progress, but starting to make my development easier (I'm slowly progressing on it):

XControl = class()

function XControl:init(position, size)
    self.position = position
    self.size = size
    self.parent = nil
    self.children = {}
    self.onDraw = {}
    
    self.pvtxc = {}
end


function XControl:pvtDraw()
    self:draw()
    
    for i,v in ipairs(self.children)
    do
        pushMatrix()
        translate(self.position.x, self.position.y)
        v:pvtDraw()
        
        for ind, drw in ipairs(self.onDraw)
        do
            drw(self)
        end
        
        popMatrix()
    end
end


function XControl:addDrawFn(fn)
    local l = #self.onDraw
    self.onDraw[l] = fn
end


function XControl:draw()
end


function XControl:addChild(inChild)
    inChild:removeFromParent()
    
    local cid = #(self.children)+1
    self.children[cid] = inChild
    inChild.parent = self
    inChild.pvtxc.childId = cid
end


function XControl:removeFromParent()
    if self.parent == nil
    then
        return
    end
    
    local pcid = table.getn(self.parent.children)
    if self.parent.pvtxc.count ~= 1 then
        self.parent.children[self.pvtxc.childId]
            = self.parent.children[pcid]
        self.parent.children[self.pvtxc.childId].pvtxc.childId
            = self.pvtxc.childId
    end
    
    table.remove(self.parent.children)
end


function XControl:pvtGetTouchedControl(pt)
    for i,v in ipairs(self.children)
    do
        if v:isWorldPointWithin(pt)
        then
            return v:pvtGetTouchedControl(pt)
        end
    end
    
    return self
end


function XControl:isPointWithin(pt)
    return (pt.x >= 0 and pt.y >= 0
            and pt.x <= self.size.x
            and pt.y <= self.size.y)
end


function XControl:isWorldPointWithin(pt)
    return self:isPointWithin(self:world2local(pt))
end


function XControl:isTouchWithin(touch)
    return self:isWorldPointWithin(vec2(touch.x, touch.y))
end


function XControl:local2world(v2)
    if self.parent == nil
    then
        return v2 + self.position
    else
        return self.parent:local2world(v2 + self.position)
    end
end


function XControl:world2local(v2)
    if self.parent == nil
    then
        return v2-self.position
    else
        return self.parent:local2world(v2 - self.position)
    end
end


function XControl:pvtOnTouched(touch)

end
And for a button...

XButton = class(XControl)

function XButton:init(position, size)
    XControl.init(self, position, size)
    
    self.onPress = nil
    self.onRelease = nil
    self.onDrag = nil
    self.onTouch = nil
end

function XButton:pvtOnTouched(touch)
    if touch.state == BEGAN and self.onPress ~= nil
    then
        self.onPress(touch)
    end
    
    if touch.state == MOVING and self.onDrag ~= nil
    then
        self.onDrag(touch)
    end
    
    if touch.state == ENDED
    then
        if self.onRelease ~= nil
        then
            self.onRelease(touch)
        end
        
        if self:isTouchWithin(touch) and self.onTouch ~= nil
        then
            self.onTouch(touch)
        end
    end
end