Template for step by step projects - your input please!

I want to publish a set of “step by step” tutorial projects for Codea, that lead a new user through building a project. Each step will be in its own separate tab and can run on its own.

I’m looking for the best way that users can run one selected tab on its own. Given that this is for relative newbies, it must avoid complications or confusion as far as possible.

Below are demos of two possible approaches which allow users to move from running one tab to running another, using parameters.

The first approach is to enclose each tab’s functions in a table so they don’t clash with each other, as demonstrated below. This is pretty clean, except the table prefixes may confuse newbies, and if they copy the code to their own projects, they will have to delete all the prefixes, so it’s not very portable.

Demo 1: https://gist.github.com/dermotbalson/6504325

The only way I can see of avoiding prefixing the functions in each tab with a table ID, is to have duplicate setup, draw etc functions in each tab. Codea will compile them all from left to right, and only run the last one compiled. So I need a way of moving the selected tab to the right. I could ask the user to drag the tab they want, to the right, but this is a bit messy and confusing for newbies, and the tab order could get jumbled.

So the approach below copies the code for them, to a special Code tab on the right. Having made a selection with a parameter, they simply need to restart to activate their new selection (using the icon at bottom of screen, so it can be done without going back to the code editor).

The advantage of this approach is very clean code which is completely portable, and I think I prefer it.

Demo 2: https://gist.github.com/dermotbalson/6504690

Comments, preferences and suggestions please, because I’d like to get it right before I plug a whole bunch of projects into it.

I prefer demo 1. A table that contains some functions seems a lot clearer concept than a program that rewrites its tabs. If you’re concerned about them reusing your tabs, than demo 1 is better too, since there’ll be no namespace issues with functions like draw() and touched()–what’ll happen when they copy the “Asteroids” tab (for example) into their own program and its draw() doesn’t work anymore. The tabs can be named meaningfully like in demo 2, not just T1,T2, etc. Just my opinion.

Yes.
You could also add at the beginning of each step tab:

Table.insert(tabName, "basics")
Table.insert(tabDemo, T1 )

So everything is defined locally, rather later.

I have an alternative for you.

I don’t like the “define everything and the last survives” method because some steps might define things that aren’t overwritten by later steps but which are then available and potentially could interfere with the later steps. Lua is “global by default” and one of the arguments against this is that it makes it very easy to create a global variable by accident and very hard to track it down.

So I prefer putting everything in its own box (table) and invoking it that way. The problem with this is that, again due to the “global by default”, everything not explicitly local should be in the table. It’s not enough to put T1.draw and T1.setup, everything else needs to be prefixed with T1. This is clearly a pain in the neck!

The solution is to use metatables. It is possible to modify the metatable of a chunk so that assignments are not global but are “locally global”. Moreover, it is possible to do this from within the chunk. The code is really quite simple. Add a tab at the start, say “Localisation”, with the following:

tabs = {}

function localise(n)
    local t= {}
    setmetatable(t,{__index = _G})
    setfenv(2,t)
    tabs[n] = t
end

function invoke(n)
    local t = tabs[n]
    draw = t.draw
    setup = t.setup
    setup()
end

Then at the top of each tab you put:

localise(<some name>)

The name is how you want to remember that tab. I tried this out with West’s snake tutorial and used numbers to index the steps. This works out well because then the Main tab can be:

function setup()
    parameter.integer("Step",1,17,1)
    parameter.action("Run the step",function() invoke(Step) end)
end

The __index = _G means that any request to something that does not exist in the current metatable gets passed up to _G. So in the tab, everything looks like it is global, but it isn’t.

When cutting-and-pasting from one project to another, all one has to do is remove the localise(<some name>) statement. If this isn’t done, it will complain and that will be a reminder to do so.

Thanks, @starblue and @Jmv38!

@Andrew_Stacey - thanks for your suggestion, which looks very cool. I’ll need to examine it carefully before I can appreciate it properly (I know something about metatables but I wouldn’t say we were best friends).

@Andrew_Stacey, @West - I modified my template to use Andrew’s approach, and it works well.

https://gist.github.com/dermotbalson/6508514

I’m happy to use this approach. How about you, @West?

Yes though when trying it with asteroids the ship appears in the wrong position due to x being set tothe screen width minus the sidebar. the same problem as your platformer demo.

I’m redoing my step by step demos so they always show the sidebar

I’ve created a page in the wiki for step by step projects, here

https://bitbucket.org/TwoLivesLeft/core/wiki/Step%20by%20step%20projects

I have a few more to add

@Andrew_Stacey, in your code above, can you explain exactly what the setfenv(2,t) does? I thought setfenv affected only the function that contains it, or some caller above it, not a global replace of the entire environment. (The “2” is referring to the global environment, right?)

@starblue It works because each tab in Codea is executed as a Lua “chunk”. So within localise then the 2 refers to the environment of the tab’s chunk.

@Ignatz Some comments on the template (though first I should say that it looks very nice in general):

  1. In the RunCode() function you have output.clear. I can’t see output defined anywhere. Did I miss it?

  2. By using a callback in parameter.integer, won’t this mean that if I go from step 1 to step 3 then the callback will also fire for step 2? I seem to remember a discussion about this not that long ago. That’s why I put a parameter.action in the original code.

  3. In the invoke function, should the draw etc be set back to their vanilla states if they aren’t defined in a tab? So draw = t.draw or vanilla.draw? (Also, the routines to change should probably be put into a table for ease of maintenance. There are also the orientation functions and keyboard functions.) I wonder if there’s a better way to handle this …

  4. The localise(<tab name>) could be turned in to if localise then localise(<tab name>) end so that cut-and-paste just works.

thanks for that, Andrew

  1. output.clear clears the print area at left

  2. I’m not too concerned about stepping through multiple tabs with the slider, when it comes to fairly simple demos.

  3. Again for demos, I’m not sure if this is needed, because one or other tab will always be running, but I can see the value for projects in general.

  4. Good idea

  1. Didn’t know that! Learn something new every day.

  2. Hmm, but still if every setup in between gets called then it’s a bit messy.

  3. Isn’t this precisely the issue someone found with one of @West’s projects? That the touched function from one step was active in one of the others?

Along those lines, what are all the functions that Codea automatically calls? That is, the functions that Codea calls according to some event or as part of its normal run and which a user might conceivable define. Here’s what I think:

  • setup
  • draw
  • touched
  • orientationChanged
  • close
  • restart
  • keyboard

Have I missed any? I saw you had collide in your list but I can’t find it in the documentation.

@Andrew_Stacey, thanks for the explanation. I wasn’t aware there was an anonymous function representing the chunk controlling the tab. I was wondering how the environment change remained in effect after the call to “localise” ended.

BTW, collide is documented under Physics/Handling Contacts Between Bodies/Contacts Overview.

@Andrew_Stacey - taking into account everything you’ve suggested, I can get as far as shown below (which also resets any events not assigned in t) . Improvements welcome.

function invoke(n,d)
    local t = tabs[n]
    --store original event pointers
  local events={draw,setup,touched,collide,orientationChanged,close,restart,keyboard,cleanup}
    setup = t.setup or events[1]
    draw = t.draw or events[2]
    touched=t.touched or events[3]
    collide=t.collide or events[4]
    orientationChanged=t.orientationChanged or events[5]
    close=t.close or events[6]
    restart=t.restart or events[7]
    keyboard=t.keyboard or events[8]
    cleanup=t.cleanup or events[9]
    setup()
end

@andrew_stacey i am getting confused: close and restart are not automatic called by codea on button event? I have tried to overwrite close and it has no effect: when i touch the close button when a program is running, my own close function is not called at all.

@Jmv38 It’s probably me that’s confused. I have code that calls close and reset from a menu and so when I overwrite them then it does have an effect.

@Ignatz Here’s what I tried that worked for me:

local fnames = {
    "setup",
    "draw",
    "touched",
    "collide",
    "orientationChanged",
    "close",
    "restart",
    "keyboard",
    "cleanup"
}
local fns = {}
for k,v in ipairs(fnames) do
    fns[v] = _G[v]
end

function invoke(n)
    local t = tabs[n]
    for k,v in ipairs(fnames) do
        _G[v] = fns[v] -- reset to original value
        _G[v] = t[v] -- overwrite with the new code
    end
    setup()
end

Nice code!

@Andrew_Stacey - =D>

I’ve updated my template for step by step projects based on the code above, here

https://gist.github.com/dermotbalson/6546692

It allows the user to choose a tab with the selector, then press a button to run it. When the selector is moved, a list of tab descriptions is printed, to help the user.