Saturday, June 30, 2012

Interlude 5 - A Twinkling Starfield Class (Updated 17/01/16)

Interlude 5.1 Setting the Scene


Before our App gets any more complicated we are going to want to add a State Machine to keep track of things. We will tackle that in the next Tutorial. For now, I would like to talk about a class which produces a twinkling star background written by Ipad41001.

The plan is to create a simple game to demonstrate the ideas that we have been talking about. It seemed appropriate to replicate one of the first games written on a computer (and one that hadn't already been done by the intrepid coders over at the Codea Forum). So we have chosen Spacewar!


Interlude 5.2 Spacewar!


Spacewar is a game with two ships in space, the aim is to destroy the other ship without getting destroyed yourself. A star in the centre of the screen pulls on both ships and requires maneuvering to avoid falling into it. In an emergency, a player can enter hyperspace to return at a random location on the screen, but only at the risk of exploding if it is used too often. The game was originally written on a PDP-1 by some coders at MIT in 1962. Apparently it took them 200 hours but they were using assembler.


Interlude 5.3 The Twinkle Class


For our Spacewar game we need a background star field. The original coders found that a made up static background was lacking in verisimilitude so they created a new background based on real star charts that scrolled slowly: at any one time, 45% of the night sky was visible, every star down to the fifth magnitude. We won't be doing that!

Now it is a lot easier to get above the crowd if you stand on the shoulders of giants, so rather than reinvent the code, we will use something already written by Ipad41001. We have changed the background colour and inserted the code into a class but apart from that it is the same as Ipad41001's original version. Note that we are currently getting a frame rate of around 30 FPS using 100 stars on an iPad 1 thus some subsequent optimisation is going to be required.

The Twinkle star field class is shown below. Copy it into a new tab in Codea called Twinkle and we will show you how to use it in the next tutorial.


Twinkle = class()

-- Written by Ipad41001
-- December 2011

function Twinkle:init(numberOfStars)
    -- you can accept and set parameters here
    st = {}
    ns = numberOfStars -- number of stars
    for i = 1,ns do
        if math.random(2) == 1 then g = .25 else g = -.25 end
        l = 5 + (math.random(39)/4)
        st[i] = {g=g,l=l,x=math.random(WIDTH),y=math.random(HEIGHT)}
    end
end

function Twinkle:draw()
    -- Codea does not automatically call this method
    pushStyle()
    rect(0,0,1,1)
    -- background(20, 46, 181, 255), this is Ipad41001 original background colour
    background(0, 0, 0)
    strokeWidth(3)
    lineCapMode(SQUARE)
    stroke(255, 255, 255, 255)
    fill(255, 255, 255, 255)
    for i = 1,ns do
        if st[i].l <= 5 and math.random(2) == 1 then st[i].g = .25
        elseif st[i].l >= 15 and math.random(2) == 1 then st[i].g = -.25 end
        st[i].l = st[i].l + st[i].g 
        ll = (15 - st[i].l)/2
        line(st[i].x-st[i].l,st[i].y,st[i].x+st[i].l,st[i].y)
        line(st[i].x,st[i].y-st[i].l,st[i].x,st[i].y+st[i].l)
        line(st[i].x-ll,st[i].y-ll,st[i].x+ll,st[i].y+ll)
        line(st[i].x+ll,st[i].y-ll,st[i].x-ll,st[i].y+ll)
        ellipse(st[i].x,st[i].y,6,6)
    end
    popStyle()
end

You can have a look at other approaches to creating a star field which have been done by Bri_G on the Codea Forum.

Friday, June 29, 2012

Tutorial 4 - A Splash Screen (Updated 17/01/16)

Webcomic Courtesy of Ethanol & Entropy

4.1 Should you do a Splash Screen?


Personally we like adding a splash screen to our Apps. However before starting todays tutorial we should point out Apple's position on this. Quoting from the iOS Human Interface Guidelines:

"Avoid using your launch image as an opportunity to provide:
  • An “application entry experience,” such as a splash screen
  • An About window
  • Branding elements, unless they are a static part of your application’s first screen
Because users are likely to switch among applications frequently, you should make every effort to cut launch time to a minimum, and you should design a launch image that downplays the experience rather than drawing attention to it."
So for all the reasons that Apple don't like a splash screen, we do. There are some lines with Apple that you can't cross and will get you rejected from the App store. This isn't one of those, We have 8 Apps in iTunes and they all have splash screens. So read up on the subject and make your own decision.
Note that even if you decide to toe the Apple line, you can use this class in less controversial instances (eg a win / lose screen or credits).

4.2 Classes You Need to Splash


Most of the classes that we will add are not strictly required to make a splash but when writing a real world App you will probably have something similar so we have included them. There is not a lot of new code required, it is more a case of stitching together what we have already covered. You will need:
If you have been following along then most of these classes will already be in your Menu project. The code for Fader and RoundBorder are shown in Interludes 3 and 4 below. The two classes we need to cover here are the changes to Main and SplashScreen.

4.3 The Main Class


Okay, let's have a look at the revised Main class first.



-- Use this function to perform your initial setup

function setup()
    
    print("Menu & Splash Screen Demo")
    
-- [[ saveProjectInfo("key", "value") allows you to assign metadate to your App. It currently supports the Description and Author keys. The value that you assign to the Description will appear under your App in the Project Browser screen. This block of code is optional but good practise. ]]

    saveProjectInfo("Description", "Menu & Splash Screen Demonstration Code.")
    saveProjectInfo("Author", "Reefwing Software")
    saveProjectInfo("Version", "1.0")
    
    -- [[ The next block of code is also optional. It will keep an eye on our Frames Per Second (FPS) as the App runs. ]]
    
    FPS = 0
    watch("FPS")
    
    timeInterval = 0
    frameCount = 0
    
    -- [[ create a new button which will represent our App menu, it wont be visible until after the splash screen fades away. ]]
    
    button = Button("  Easy Levels  ")
    button.action = function() buttonPressed() end
    
    -- [[ Create the splash screen. The variables passed to the SplashScreen class are text to display and the fading speed of the flash screen. The larger the fading speed the faster it fades. One is the minimum speed. ]]
    
    splashScreen = SplashScreen("Splash Screen Demo", 1)
    
end

-- This function gets called once every frame

function draw()

    -- This sets a black background color 
    
    background(0, 0, 0)

    -- Do your drawing here
    
-- [[ This block of code is optional. It calculates and displays every second the Frames Per Second (FPS) rate that our App is drawing at. ]]

    frameCount = frameCount + 1
    timeInterval = timeInterval + DeltaTime
    
    if timeInterval > 1 then
        FPS = math.round(frameCount / timeInterval)
        timeInterval = 0
        frameCount = 0
    end
    
    -- [[ Draw Splash Screen and when it has finished fading (splashScreen.shown = true) the menu button(s). ]]
    
    if splashScreen.shown == true then
        drawButton()
    else
        splashScreen: draw()
    end
            
end

-- [[ Button related functions - described in earlier tutorials. ]]

function drawButton()
    button.pos = vec2(400, HEIGHT/2)
    button:draw()
end

function buttonPressed()
    
    print("Button Pressed")
    
end

function touched(touch)
    
    button:touched(touch)
    
end

-- [[ This function was written by Vega and is used in the FPS calculation. It can be deleted if you are not including the FPS code. ]]

function math.round(num)
    return math.floor(num + 0.5)
end

4.4 The SplashScreen Class


You can tailor this class to include whatever you want to show on your splash screen. We picked some obvious things like text and a logo. To add a bit more pizzaz we added a rounded border.


SplashScreen = class()

function SplashScreen:init(splashText, fadeSpeed, logoURL)
    
    -- you can accept and set parameters here

-- [[ Those sharp eyed may notice that we only passed in two parameters from our Main class to this class. Consequently Lua will set logoURL to nil. We did this to demonstrate this point. For the tutorial we have hard coded the logo loaded but we will show you in the code below where you can change this. Hopefully most of the variables below are self explanatory. ]]
    
    self.splashText = splashText
    self.fadeSpeed = fadeSpeed
    self.logoURL = logoURL
    self.shown = false

-- [[ We created some colour name variables to make the code more readable. Andrew Stacey has produced a mammoth list of colour names, included in the Roller Coaster example App which comes with Codea. The class is appropriately called ColourNames. You could copy this into your projects if you want to use the names that he has defined. It is overkill for our purposes. ]]
    
    blackColour = color(0, 0, 0)
    redColour = color(255, 0, 46, 255)
    blueColour = color(0, 188, 255, 255)

-- [[ Start by initialising our splash screen border. This class is described in Interlude 4. ]]
    
    splashBorder = RoundBorder(10, 10, WIDTH - 10, HEIGHT - 10, 1, blueColour, blackColour)
    
    -- init Fader
    -- the larger the fade speed the quicker the fade
    
    fader = Fader(fadeSpeed)
    
    if fader.fading == false then
        fader: fade(fadeAnimationDone)
    end
    
-- [[ The next bit of code comes from Simeon's Image IO example project which is included with Codea. In particular the parts related to downloading an image and displaying it. In your App you would more likely use a locally stored image so that it would be displayed regardless of whether you have an internet connection. ]]

     -- Start the logo request, didGetLogo is our callback function
    
    logo = nil
    
-- [[ The next line of code is from the original example and will download the Two Lives Left logo. In a blatant example of self promotion we have replaced this with the Reefwing Software logo. Feel free to insert your own logo URL here. Note depending on the size of the logo used you may have to tweak where it is drawn. To make this class more general and remove the hard coded logo, you can replace this text with self.logoURL. Just make sure you add this variable in the Main class when you initialise the splash screen. didGetLogo is called when the http request finishes. ]]

    --http.request( "http://twolivesleft.com/logo.png", didGetLogo )
    
    http.request( "http://www.reefwing.com.au/Reefwing_Software/Home_files/reefwing-logo-%28black-on-white%29.png", didGetLogo )
    
end

function SplashScreen:draw()
    
    -- Codea does not automatically call this method
    
    pushStyle()
    
-- [[ Set the background black and define the splash text. We haven't drawn it yet. ]]

    background(blackColour)
    font("Arial-BoldMT")
    fill(blueColour)
    fontSize(72)
    textWrapWidth(300)
    textAlign(CENTER)
    
    local w,h = textSize(self.splashText)
    
    -- Detect if the splash screen has started fading.

    if fader.fading == true then
        
-- [[ Start by drawing the border. The order that you draw is important. Drawing this last would make the text and logo hidden. ]]

        splashBorder: draw()
        
        -- Draw the logo we downloaded (when it's ready!)
        
        if logo ~= nil then
            sprite( logo, WIDTH - logo.width/2 - 20, logo.height)
        end
        
        -- Finally draw the text.

        text(self.splashText, WIDTH/2, HEIGHT/2)
        
        fader: draw()
    else
        self.shown = true
    end
    
    popStyle()
end

function fadeAnimationDone()
    
    -- Call back function for Fader complete
    
    print("Splash Screen Fade Complete")
    
end

-- [[ The next bit of code also comes from Simeon's Image IO example project which is included with Codea. ]]

function didGetLogo( theLogo, status, headers )
    print( "Response Status: " .. status )
    
    -- Store logo in our global variable
    logo = theLogo
    
    -- Check if the status is OK (200)
    if status == 200 then
        print( "Downloaded logo OK" )
        print( " Image dimensions: " .. 
                    logo.width .. "x" .. logo.height )
    else
        print( "Error downloading logo" )
    end
end

Interlude 4 - A Rounded Border Class (Updated 17/01/16)


Interlude 4.1 Yes it is a bit of a Hack...


For our splash screen Tutorial we wanted to include a nice rounded border. To that end the following extends the roundRect() function that we have used in previous tutorials (to create a button). There are much more elegant ways to achieve this (e.g. using a mesh) but this has the advantage of simplicity. Once we have covered meshes we will rewrite this class. Another alternative would be to extend the roundRect() function to include a variable which determines if the roundRect is filled or not.


Interlude 4.2 The Rounded Border Class


This class relies on the roundRect() function, so you must include that to use RoundBorder(). The concept is very simple. We just draw two rounded rectangles, one inside the other. The inner rectangle is smaller by a factor of borderWidth. The larger rectangle is drawn using the borderColour and the inner using the fillColour.



RoundBorder = class()

function RoundBorder:init(x, y, w, h, borderWidth, borderColour, fillColour)

    -- you can accept and set parameters here
    -- Set dimensions of the outer "border" round rectangle

    self.x = x
    self.y = y
    self.w = w
    self.h = h
    self.r = 30

    self.borderWidth = borderWidth
    self.borderColour = borderColour
    self.fillColour = fillColour
    
    -- Set dimensions of the inner "background" round rectangle

    inX = self.x + self.borderWidth
    inY = self.y + self.borderWidth
    inW = self.w - 2 * self.borderWidth
    inH = self.h - 2 * self.borderWidth
end

function RoundBorder:draw()
    -- Codea does not automatically call this method
    -- Do the right thing and save the graphic context
    
    pushStyle()
    
    -- Start by drawing the outer Rounded Rectangle, this will become the border.
    
    fill(self.borderColour)
    roundRect(self.x, self.y, self.w, self.h, self.r)
    
    -- Then draw the inner rectangle using the fill colour
    
    fill(self.fillColour)
    roundRect(inX, inY, inW, inH, self.r)
    
    -- Return the graphic context to the way it was when entering this function
    
    popStyle()
end

Interlude 3 - A Fader Class (Updated 17/01/16)

Interlude 3.1 Transition Effects


This Interlude was originally part of Tutorial 4 but we split it out because it is useful from a stand alone perspective and Tutorial 4 was getting too long.

There will be times when your App needs to transition from one screen to another and effects are useful in letting the user know what is happening. The following is one approach to what transition to use and when.
  1. Fade: The content is the same but users change their view of the content (for example, switching between day and week view in a calendar or switching between viewing a list of images and viewing the thumbnails of the same images).
  2. Slide: Users move to an item at the same level in the navigation hierarchy.
  3. Zoom: Users move to an item at a different level in the navigation hierarchy (for example, from a parent item to a child item or a child item to a parent item). Users create a new item, send an item, or save an item.
iOS has a bunch of built in transition effects but unfortunately (to our knowledge) they are not currently available in Codea. We will be using a fade transition in the Splash Screen tutorial, so let's have a look at one way of achieving this.


Interlude 3.2 A Fade Transition Effect


Vega over on the Codea forums has once again done the hard work for us. He produced a class which can fade in or out a screen. We have simplified the code to just fade out a screen but you can see the original class at the link above. Create a new tab in your project and paste in the following code. We will show you how to use it in Tutorial 4.


Fader = class()

function Fader:init(fadeSpeed)

--[[ fadeSpeed determines how quickly the screen fades. 1 is the lowest speed available and will give the slowest fade effect. The bigger the number the faster the fade. The self.fading variable is used to detect whether the class is in the middle of fading something and self.alpha is used to determine the transparency of the rectangle covering the screen. An alpha of zero is transparent, and it can go up to 255 which is solid. ]]

    self.fading = false
    self.alpha = 0
    
    if fadeSpeed > 0 then
        self.fadeSpeed = fadeSpeed --raise this number to fade faster
    else
        self.fadeSpeed = 1
    end
end

function Fader:fade(func)

--[[ func is the function that this class will call once the fade transition is complete (assuming it exists and isn't NIL. The Fader: fade() function is called to start the fading transition. ]]

    self.fading = true
    if func ~= nil then 
        self.func = func 
    else
        print("Warning:: Fader call back method is NIL.")
    end
end

function Fader:draw()

-- If it is time to fade then...

    if self.fading then

-- [[ Do the right thing and save the current screen settings, we will return these at the end using popStyle(). No smooth and no stroke are used to ensure we get an acceptable frame rate. Without these two statements the animation will appear too slow, particularly on an iPad 1. ]]

        pushStyle()                      
        noSmooth()                      
        noStroke()

-- [[ fill is set to black and to the current alpha. Initially alpha will be zero. i.e. transparent and get gradually more solid. As Vega mentions in the comment below, if you want to fade to some other colour then change this fill statement. ]]

        fill(0, 0, 0, self.alpha)
        
        --change that color if you want to fade to a color other than black
        
-- [[ The basic approach is to create a rectangle in the fill colour which covers the entire screen. For our fade out example, the rectangle is initially transparent and gets more solid each draw cycle by the fadeSpeed. Remember the draw() function gets called 60 times a second if possible. ]]

        rect(0,0,WIDTH,HEIGHT)
        popStyle()
        self.alpha = self.alpha + self.fadeSpeed
        
-- [[ The maximum alpha value is 255. This indicates a solid colour and when we reach it the fade is complete. So at this stage self.fading becomes false and the transition end call back function is called (if defined). ]]

        if self.alpha >= 255 then 
            self.alpha = 255 
            self.fading = false --fade complete
            if self.func ~= nil then self.func() end -- its all black, so switch the frame
        end
    end
end

Saturday, June 23, 2012

Interlude 2 - Codea Keyboard Buttons & Editing (Updated 10/1/16)

Webcomic Courtesy of Ethanol & Entropy

Interlude 2.1 Custom Buttons


The latest release of Codea (2.3.2) added the ability to search your code. A side effect of this is I discovered what another one of the Codea custom keyboard buttons did. Like most folks I had been using a fairly haphazard trial and error approach to working out what each one of the buttons did but this is tempered somewhat by the fear that I might push the armageddon button.

So for my benefit as much as anyone else I decided to list all of the custom keyboard buttons here and describe what they do. Note that some of these keys are context sensitive and some will pop up an action dialog box.


Figure 1

Going from left to right, the numbers below refer to the eleven custom buttons shown in Figure 1.
  1. Indent Code - This will indent the text to the right of the cursor four spaces. If you select a block of code then this key will indent the lines selected by four spaces.
  2. Undo - This will undo the last action.
  3. Codea Reference Documents (Figure 3) - This button will popup the Codea Reference Documents. Note that if you select a function using tap and hold, then one of the options in the subsequent cut, copy, and paste dialog will be lookup. Tapping this will open the same reference document dialog.
  4. Numeric Function Popup - As shown in Figure 2 this will popup a dialog from which you can choose to insert: square brackets, parenthesis, minus, plus, multiplication, division, modulas, hash, tilda, less than or greater than symbol at the cursor location.
  5. Cursor Left or Right - Tapping this button will move the cursor one space left or right, depending on which side of the button you tap. Using a finger it is sometimes difficult to  precisely locate the cursor, particularly in the middle of a word. This button can help with that.
  6. Select Text Left or Right - Similar to the previous key this one will move the cursor one position left or right depending on which side of the button you tap. In addition, it will also select the text as the cursor moves.
  7. Insert Brackets - This button will insert rounded brackets at the cursor location.
  8. Insert Quotes - Another obvious button, this one will insert quotes at the cursor location.
  9. Function Lookup and Search (Figure 4) - This button will provide you with a popup dialog with a list of functions in your program. Tapping on a function in the list will jump you to that position in your code. You can also search for a word in your code using the search bar. The search will only start after you type three characters.
  10. Equals - this is the same as the equals key. It is placed here as a convenience since you use it a lot.
  11. Run Program - this key operates the same as the arrow in the bottom right of the coding screen. It will attempt to compile and run your program.

Interlude 2.2 Editor Gestures


Thanks to Keebo for pointing out that there are also gestures available on the Editor Screen. 

If you swipe up or down on the Cursor Left/Right Key (the key numbered 6 in Figure 1), then the cursor will move up or down one line, in the direction of your swipe.

In addition, if you swipe left or right on the main screen this will move the cursor left or right one space.

Interlude 2.3 Undo


The undo function in Codea is a bit hard to find. If you press the ".?123" button on the default iPad keyboard this button will be shown in the bottom left corner.  

Interlude 2.4 The Split Keyboard


If you use the iPad keyboard a lot you may accidentally cause it to split to either side of the screen. This is a "feature" introduced in a recent version of iOS. Personally we don't like it. You can turn it off as an option in the Settings App. Go to Settings -> General -> Keyboard -> Split Keyboard. Thanks to dave1707 on the Codea Forums for the following explanation on how to use this feature.

"To turn the split keyboard on (assuming this option is enabled in Settings), hold your finger on the hide keyboard button (lower right), and a pop up will appear. This has 2 options, undock and split. If you press undock, the keyboard will move up to the middle of the screen. If you press split, the keyboard will split in half. 

If you press and hold the hide keyboard button while in those modes, you will get a popup that says, Dock and Merge. Pressing that, the keyboard will go back to normal size at the bottom of the screen. Also, with the keyboard in normal size, if you place your finger and thumb together on the middle of the keyboard and then move them apart towards the sides, the keyboard will split. Placing your thumb and finger spread apart on the keyboard and moving them together, the keyboard will close."



Figure 2


Figure 3


Figure 4


Friday, June 22, 2012

Tutorial 3 - A Simple Button Class (Updated 10/1/16)


Webcomic Courtesy of Ethanol & Entropy

3.0 Creating a Button in Codea


A control that you will use frequently is a button. Now that we have the foundations sorted we can punch out a simple button quite easily. Mostly because a sample button class is provided with Codea. There is a bit of cutting and pasting involved but we will go through each line of the Button() class so you can understand what is what.

Whip back to our old friend the Sounds Plus example project and copy all of the contents of the Button class tab. Back in your Menu project, create a new tab, call the Class Button and paste the code you just copied. Refer to Tutorial 2 if you have forgotten the exact steps.

Your Menu project should now contain 3 tabs: Main, RoundRect and Button. We will have a look at the Button class first. I have inserted a bunch of additional comments (in blue) so you can understand what is happening.

3.1 The Button Class


Button = class()

-- [[ There are no classes in standard Lua however they are handy concepts so Codea includes a global function called class() which provides equivalent functionality. You can read more about Codea classes in the wiki ]]

function Button: init(displayName)

-- [[ The Init function gets called before setup(). This is where you define and initialise your class and its member variables. The class variables are fairly self explanatory but for completeness: displayName: Is the text displayed on your button. The button will scale up and down to fit the text. pos: Defines the x and y - coordinates of the button using a vector. size: Is a vector which contains the width and height of the button, which is set by the display name text, and is used to determine if a button has been hit.  action: Is the function that you want called when the button is tapped. color: Is the color of the button fill. ]]

    -- you can accept and set parameters here

    self.displayName = displayName
    
    self.pos = vec2(0,0)
    self.size = vec2(0,0)
    self.action = nil
    self.color = color(113, 66, 190, 255)

end

function Button:draw()

-- [[ Your main code needs to explicitly call this function to draw the button, it won't happen automatically. We will see how this works when we update the main() class. ]]

    -- Codea does not automatically call this method

    pushStyle()

-- [[ pushStyle() saves the current graphic styles like stroke, width, etc. You can then do your thing and call popStyle at the end to return to this state.]]

    fill(self.color)

-- [[ fill is used initially to set the colour of the button, then the font type and size is set. You could change this in your implementation of the button class if you wish. Click here to see the available fonts. ]]
    
    font("ArialRoundedMTBold")
    fontSize(22)
    
    -- use display name for size

    local w,h = textSize(self.displayName)
    w = w + 20
    h = h + 30
    
-- [[ As stated in the code, displayName is used to size the button and then we use the class we looked at in Tutorial 2 to draw a rounded rectangle. ]]

    roundRect(self.pos.x - w/2,
              self.pos.y - h/2,
              w,h,30)
            
    self.size = vec2(w,h)

-- [[ Note that class variables are designated using the self keyword. e.g. self.size. The next block of code sets the colour of the button text and its position on the button. ]]
            
    textMode(CENTER)
    fill(54, 65, 96, 255)
    text(self.displayName,self.pos.x+2,self.pos.y-2)
    fill(255, 255, 255, 255)
    text(self.displayName,self.pos.x,self.pos.y)
    
-- [[ Return the graphic style to what it was before you entered this function. This is considered polite behaviour for a function because it can be hard to track down if the style is being changed deep within some function and you don't want it to. ]]

    popStyle()

end

function Button:hit(p)

-- [[ This function works out if the last touch (after you lift your finger) was on this button, using the size and pos variables. Returns true if it was and false if it wasn't.  The local keyword defines a local variable. Unlike global variables, local variables have their scope limited to the block where they are declared. A block is the body of a control structure, the body of a function, or a chunk (the file or string with the code where the variable is declared). ]]

    local l = self.pos.x - self.size.x/2
    local r = self.pos.x + self.size.x/2
    local t = self.pos.y + self.size.y/2
    local b = self.pos.y - self.size.y/2

    if p.x > l and p.x < r and
       p.y > b and p.y < t then
        return true
    end
    
    return false
end

function Button:touched(touch)

    -- Codea does not automatically call this method

-- [[ As with the draw() function the touched function is also not called automatically by your code. If you don't call this then you won't know if someone has tapped your button. It reminds me of the old joke, "what do you call a boomerang that doesn't come back?" ..."A stick!" The test, if self.action checks whether you have defined a function to call when the button is tapped. If self.action is nil then nothing will happen.]]

    if touch.state == ENDED and
       self:hit(vec2(touch.x,touch.y)) then
        if self.action then
            self.action()
        end
    end
end


3.2 The Main Class


Now that you are an expert on the Button Class, we can have a look at what is required in your Main Class to instantiate and use a button. It is fairly simple.

-- Use this function to perform your initial setup

function setup()

    print("Button Test Project")
    
-- [[ Create a new button, it wont be visible until you draw it. The init of the button will also set the displayName. You can change this later if you wish by changing the string assigned to button.displayName. The action variable is assigned the function you want to call when the button is tapped. We haven't attempted to be too ambitious with this first attempt.]]
    
    button = Button("Press Me")
    button.action = function() buttonPressed() end
    
end

-- This function gets called once every frame

function draw()

    -- This sets a dark background color 

    background(40, 40, 50)

    -- Do your drawing here, drawButton is defined below.
    
    drawButton()
    
end

function drawButton()

-- [[ Draw the button at some arbitrary spot on the screen and then call the buttons draw() function. You MUST include this step within the Main draw() function. ]]

    button.pos = vec2(400, HEIGHT/2)
    button: draw()

end

function buttonPressed()
    
-- [[ This is where the action happens. Whenever the button is tapped, this function will be called. You can call it whatever you want but it must match the function that you assign to the button.action variable. We aren't doing anything too exciting here but it should illustrate the point. ]]

    print("Button Pressed")
    
end

function touched(touch)

-- [[ Like the button draw() function this is another one that you MUST call for the button to work. It passes the touch detected in the main class to the button class to see if it needs to do anything with it. If the button detects a hit then the action function gets called. ]]
      
    button:touched(touch)
     
end
   
You can download a copy of the files from here.
   

3.3 An Alternative Approach


You now know how to implement a button and assign an event handler for when it gets tapped.

There are a number of other approaches that you can take to solve this problem. Over on the Codea forum Bri_G, Maxiking16 and Reldonas have all contributed sample code to help make your buttons look even sexier. 

3.4 Other Alternatives (Mesh or Sprites)


Vega has come up with a button class which uses meshes to generate the buttons. This class includes buttons in the Apple style, Windows style and customised buttons. And ChrisF has come up with another approach which uses sprites.

Interlude 1 - Delay / Sleep & Display Frame Rate (Updated 3/9/15)

Version Information


This tutorial has been updated for Codea version 2.3.1(47). You can check your version of Codea by tapping on the Two Lives Left logo in the bottom middle of the Codea start screen (Tutorial 1, Figure 1).


Interlude 1.1 Delay and Frame Rate


A question on the Codea Forums got me thinking about how you might slow things down if your code is animating too quickly. There is no sleep or delay function in Lua so initially I thought something like the following would work.

function delayForSeconds(secs)
    secs = secs or 1
    local endTime = os.time() + secs
    while os.time() < endTime do end
end

This doesn't actually work on my iPad unless you use a delay of greater than 65 seconds. Which is not particularly useful. Why is this so?

Well if you whip out your handy print() function and have a look at what os.time() returns you will see that it is a VERY large number (something like 1.34e+09 which is the number of seconds since the EPOCH or possibly something else...). A quick visit to the Codea Reference will tell you that the number you get backs depends on your system. There is probably something useful that you can do with this but Codea provides easier ways.

So next I looked at DeltaTime and ElapsedTime. Note that the trick with using these is they only change each time draw() gets called so you can't use them in separate function and wait for these to change to the value you are looking for.

The approach I ended up with is shown in the code example below which prints out the frame rate every second. Remember in Tutorial 1 I said that Codea tries to call the draw() function 60 times per second, this code will show you what is actually being achieved. if you have a complex program your frame rate may be less than 60.

function setup()
    initTimeValue = ElapsedTime
    frameCounter = 0
end

function draw()
    if (ElapsedTime - initTimeValue < 1) then
        frameCounter = frameCounter + 1
    else
        print("Frames per second: "..frameCounter)
        frameCounter = 0
        initTimeValue = ElapsedTime
    end
end


Interlude 1.2 A Better Approach to Displaying Frame Rate


The problem with the previous approach to displaying frame rate for your program is that it is continually printing to the console. This makes it difficult if you are printing out other variables or status because they keep scrolling off the screen.

A better approach is to use the parameter.watch() function which is designed specifically for monitoring variables in real time. Here is the revised code.

function setup()

    -- FPS is the variable which contains our Frames Per Second calculation

    FPS = 0
    parameter.watch("FPS")

    timeInterval = 0
    frameCount = 0

end

function draw()

    -- Set the background to Black

    background(0, 0, 0)

    -- Calculate Frames Per Second (FPS)

    frameCount = frameCount + 1
    timeInterval = timeInterval + DeltaTime

    if timeInterval > 1 then
        FPS = math.round(frameCount / timeInterval)
        timeInterval = 0
        frameCount = 0
    end

end

function math.round(num)

-- There is no math.round in Codea currently.
-- Thanks to @Vega for providing this function

    return math.floor(num + 0.5)

end

Interlude 1.3 An Even Simpler Approach to FPS


function setup()


-- FPS is the variable which contains our Frames Per Second calculation

    FPS = 0
    parameter.watch("FPS")

end

function draw()


    -- Set the background to Black

    background(0, 0, 0)

    -- Calculate Frames Per Second (FPS)
    --
    -- DeltaTime = time in seconds since the last frame was drawn,
    -- it is a number provided by Codea.


    FPS = math.round(1/DeltaTime)

end

function math.round(num)

-- There is no math.round in Codea currently.
-- Thanks to @Vega for providing this function

    return math.floor(num + 0.5)

end

If we add this to our rounded rectangle app from Tutorial 2 it will look like Figure 15.


Figure 15. RoundRect with FPS.

When we run the updated program, you will be able to see (Figure 16) that drawing a rounded rectangle runs at 60 frames per second or thereabouts as we would expect - since it isn't a very complicated rendering exercise. You will need to scroll down to see the FPS parameter as it was the last one that we added.


Figure 16. FPS Displayed

Monday, June 18, 2012

Tutorial 2 - Creating a Rounded Rectangle (Updated 2/9/15)


Webcomic Courtesy of Ethanol & Entropy

Version Information


This tutorial has been updated for Codea version 2.3.1(47). You can check your version of Codea by tapping on the Two Lives Left logo in the bottom middle of the Codea start screen (Tutorial 1, Figure 1).

2.0 Drawing a Rounded Rectangle


Ok so our aim in these tutorials is to create code and classes that can be reused. We are working up to a standard menu class which can be used in your programs. But we need to build some foundations first, and the good people at Two Lives Left have provided most of the sample code we need. Curious?

Now menus need buttons and buttons need a rounded rectangle, so let's start there. Open up the Codea example program called Sounds Plus (Figure 7 - you will need to scroll down a bit) by tapping on it. 


Figure 7. Tap on Sounds Plus Example

One of the tabs in this project is called RoundRect, which is exactly what we need. Tap on this tab. We want to copy this entire class and use it in our new Menu project. To do this, tap and hold on any of the text in this class to bring up the Selection pop up, then tap on Select All, and then Copy. Dismiss the keyboard and then tap the back button (<) in the top left corner.

Back at the Codea launch page, tap on Add New Project and call it Menu (or whatever else you want). You will have one class called Main which you would have seen in Tutorial 1. Tap on the + in the top right corner and then tap on Create New Class (Figure 8). 


Figure 8. Create a New Class.

Call the new class RoundRect and then tap done. This will generate three boiler plate functions (init, draw and touched) which we don't need. Tap and hold on any of the text in the class, Select All and tap the delete key on the keyboard. Tap and hold on the empty screen to bring up the Paste pop up and tap that. Make sure you don't tap Cut to remove the old text otherwise you will overwrite the code you copied and will paste back what you just tried to delete.

You should now have the RoundRect class (Figure 9) which you copied from the Sounds Plus example code. 

Figure 9. RoundRect Class

Let's have a bit of a look at the rounded rectangle class. I have inserted comments in green to help you understand what is happening.

The roundRect function has 5 variables. These are: roundRect(x, y, w, h, r)

x - the x co-ordinate of the lower left corner of the rounded rectangle
y - the y co-ordinate of the lower left corner of the rounded rectangle

w - width of the rounded rectangle
h - height of the rounded rectangle

r - radius of the corners

function roundRect(x,y,w,h,r)

-- [[ pushStyle() saves the current graphic styles like stroke, width, etc. You can then do your thing and call popStyle at the end to return to this state.]]

    pushStyle()

-- [[ insetPos and insetSize contain the co-ordinates for the internal "fill" rectangle. InsetPos.x = x, insetPos.y = y, insetSize.x = w and insetSize.y = h. In effect this creates a rectangle that is smaller than a factor of "r" within the rectangle co-ordinates specified in roundRect. ]]
    
    insetPos = vec2(x+r,y+r)
    insetSize = vec2(w-2*r,h-2*r)
    
-- Copy fill into stroke

-- [[ Since Codea 1.3 you can retrieve the style information from all style functions by calling them without arguments. This way you only have to set the fill style once as you would for the normal rectangle function. You can read all about how this rounded rectangle function evolved on the Codea Forums.]]

    local red,green,blue,a = fill()
    stroke(red,green,blue,a)
    
-- [[noSmooth() will disable smooth (unaliased) line drawing. It is useful for drawing thin lines. This initial rectangle is used to fill in the centre of your rounded rectangle, it has the usual 90 degree corners. Four lines are then drawn around this to give the rounded corner look. You can see this yourself by commenting out the 4 lines drawn below. ]]

    noSmooth()
    rectMode(CORNER)
    rect(insetPos.x,insetPos.y,insetSize.x,insetSize.y)
    
    if r > 0 then

-- [[ You have to use smooth() if you want to use the ROUND option for lineCapMode. Four lines are now drawn around the filler rectangle. One on each edge. Because the lines have rounded ends when you overlap them it makes the corners look rounded, albeit a bit like a ball if you get the proportions wrong. Each of the lines are twice the width of the corner radius. ]]

        smooth()
        lineCapMode(ROUND)
        strokeWidth(r*2)

        line(insetPos.x, insetPos.y, 
             insetPos.x + insetSize.x, insetPos.y)
        line(insetPos.x, insetPos.y,
             insetPos.x, insetPos.y + insetSize.y)
        line(insetPos.x, insetPos.y + insetSize.y,
             insetPos.x + insetSize.x, insetPos.y + insetSize.y)
        line(insetPos.x + insetSize.x, insetPos.y,
             insetPos.x + insetSize.x, insetPos.y + insetSize.y)            
    end

    popStyle()

end

So let's take our copied class for a spin. On my iPad AIR, the dimensions of the drawing screen is:

Width - 749 pixels
Height - 768 pixels

Sidebar: You can find out your screen dimensions by adding the following two lines to the Hello World project that you created in Tutorial 1. Stick these in the setup() function (Figure 10).


Figure 10. Determine the WIDTH & HEIGHT of your screen.

print("Screen Width: "..WIDTH)

print("Screen Height: "..HEIGHT)

One of the fab things about Lua is its ability to simply concatenate a string and a number using .. as shown in the print statement above. There is no need to convert the number to a string, it happens automagically.

Sidebar Update: As Hillary mentions in the comment below the iPad 3 has the same screen dimensions in the STANDARD display mode as the iPad 1 and 2. I should have mentioned that Codea lets you set one of three display modes using the function displayMode() - obvious huh. The three display modes available are:

1. STANDARD;
2. FULLSCREEN; and
3. FULLSCREEN_NO_BUTTONS (which hides the back, pause and play buttons).

In FULLSCREEN landscape mode the iPad 1 & 2 has screen dimensions of:

Height: 768 pixels
Width: 1024 pixels

You can check this out yourself by sticking displayMode(FULLSCREEN) in the setup() function of the Hello World project that we did in Tutorial 1 and using text() in the draw() function to display the HEIGHT and WIDTH constants - note the output from print() is not visible in FULLSCREEN mode.

The iPad 3 has double the resolution of the earlier versions, i.e. in FULLSCREEN landscape mode -

Height: 1536 pixels
Width: 2048 pixels 


In Interlude 13 we will discuss this subject in a lot more depth, skip ahead and have a read if you are interested.


Tap on the Main tab in your Menu class and below the comment -- Do your drawing here add the following line of code. Note that the variables chosen aren't anything special, we are just trying to draw the rectangle near the centre of the screen. Try modifying the variables yourself to see the effect. See Figure 11.

roundRect(WIDTH/2 - 150, HEIGHT/2, 300, 20, 30)


Figure 11. Using the RoundRect Function.

Run the program and you should end up with a gray rounded rectangle on your drawing screen (Figure 12). In the next tutorial we will turn this rectangle into a button.

Figure 12. A Rectangle with rounded corners!

Happy Coding.

SURPRISE SPECIAL BONUS SECTION:

Well not really that special - I should have mentioned that Codea is designed for fast prototyping and comes with some groovy functions to assist with this, namely parameter.

parameter.number(name, min, max) will populate your run screen with a visual slider which adjusts the value of the global variable called name between the min and max values specified, starting at the initial value. The difference between parameter.number and parameter.integer is that parameter.number gives you a float variable while parameter.integer will give you an integer. You usually stick these in the setup() function of your Main class.

Figure 13. RoundRect with Parameters.

There are a couple of example projects provided with Codea which illustrate the use of parameters but let's give them a spin on our rounded rectangle class. Update the Main class in our menu project to look like the following (Figure 13).

function setup()

    print("Rounded Rectangle Fast Prototype")
    
    -- [[parameter.number provides a float parameter(name, min, max, initial, callback). Don't worry about callback at the moment, we will use this later to respond to changes in the parameter.]]

    parameter.number("x", 0, WIDTH, WIDTH/2 - 150)
    parameter.number("y", 0, HEIGHT,  HEIGHT/2) 
    parameter.number("w", 0, WIDTH, 300)
    parameter.number("h", 0, HEIGHT, 21)
    parameter.number("r", 0,  200,  30)

end

-- This function gets called once every frame

function draw()

    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    
    roundRect(x, y, w, h, r)
    
end

Now run the program and note how moving the sliders changes the variables which effect your rounded rectangle. You can see how easily you could play with this to get exactly the shape you were after. You could then note down the variables required to reproduce that shape in your code. You will need to scroll up the parameter list to see r (Figure 14).


Figure 14. RoundRect with Parameters.


2.1 An Alternative Approach


@Jordan has come up with an alternative approach to drawing rounded rectangles using an ellipse for the corners. His code is available over at the Codea Forums.