Hi Codeans,
(Sorry, this turned into a bit of a monster post!)
Having just tried stuff out in Codea for a couple of months, looking for the killer idea to actually get a concrete project off the ground a friend of mine just proposed a great game idea, so we are full steam ahead!
I am working on the UI side inwards, e.g. Intro screens, graphics etc. ā core game engine, AI etc., and he is working from the other endā¦
I have now setup the entire infrastructure and completed the Intro screen and am moving on from there. Having worked in object-oriented programming for more years than I care to admit to, Iāve been trying to apply OO principles and coding best practice in the Codea/Lua world.
I donāt expect a lot of this stuff is new to many of you, but Iāve found what I think are some quite nice ways of doing certain things, and Iād be interested in other peoples take on this as Iām new to Lua/Codea, maybe this would ultimately make a good Wiki pageā¦
Caveat: Some of this stuff is just personal preference, especially code style opinions, so donāt beat me too hard ;))
Caveat2: Code examples are pseudo-code-ish, they may not be exactly correct as Iām not writing this on the iPad.
Anyway, to kick off:
Code Style
For ease of readability for people who donāt know your code, itās a good idea to make bracketing and indenting clear, e.g. I always use brackets in āifā statements and arithmetic statements, and indent and break out long statements e.g.:
if(obj ~= nil) then end result=((var1/100)*var2) + ((var3-var4)/var5) tween(4, obj, { width = WIDTH/2, height = HEIGHT/2 }, tween.easing.linear, function () -- do stuff end ) ``` If you download the free version of Intellij IDEA (a Java development environment for Windows and Mac) then you can get a Lua plug-in. You can do a bit of prototyping for non-Codea specific stuff (you need Lua installed) but I mainly use it for code reformatting. Using AirCode I copy-paste my code into it, reformat (Ctrl-Alt-L) then copy back. sorts out all indenting and spacing which is nice ;). Constants Avoid 'magic numbers/strings' in code, they are EVIL! Anywhere you find yourself using the same number or string for the same thing then make it a constant. The easiest way to do this is to declare a 'namespace' Lua file as, e.g.:Constants = {} Constants.HALFHEIGHT = HEIGHT/2 Constants.GAMENAME = "That's It I'm Rich" ``` You can create sets of constants in this way depending on requirements... However, as we know, anyone can change these constants on the fly by just reassigning to them:Constants.GAMENAME = "This Sucks" ``` ...so we lead into: Namespaces and Classes and Data 'Protection' This is the biggest area of interest in my view, and in the forum, discussions about 'what the heck are Classes and why do I care?' are fairly frequent...My take is: Any object you need which holds specific data AND requires specific operations on that data AND you need to create MORE THAN ONE of those objects, all with their own data 'state' SHOULD be a class Any object you need only ONE of should be a table 'namespace':Constants = {} local var = nil Constants.HALFHEIGHT = HEIGHT/2 Constants.GAMENAME = "That's It I'm Rich" Constants.doThing = function(thing) var = thing end ``` This is a global object, has data, has functions and can have local variables which are not accessible from outside, as above. This is looking nice, but there is a slight problem. If you want (as I did!) to have a tweening utility method in my global class, I couldn't tween the local variables, because they don't belong to a table. If they DO belong to the main table then they are directly accessible externally... So to protect data, and to organise it internally, you can create an internal 'implementation' table object, e.g.:Utility = {} local impl = {} impl.var = 255 impl.tweenId = nil Utility.tweenMeUp = function(time, callback) impl.tweenId = tween(time, impl, {var=0}, tween.easing.linear, callback) end Utility.stop = function() if(impl.tweenId ~= nil) then tween.stop(impl.tweenId) end end ``` Your object now has internal data which it can manipulate and hold state in the 'impl' object, but none of these values are externally accessible... To make them accessible, just provide functions like set/getTweenId() to set or return the 'Impl' variable. This way YOU control what is exposed and what is not... You can do the same thing in classes also if you want per-class constants (similar to static variables in other languages). One caveat is that if one of your impl variables is itself a table and you return that from a getter function then that can be changed underneath you... You can also implement the classic Singleton pattern if you want to enforce the creation of only one instance of a class, shout if you want details, or Google ;). The above may seem like labouring the point a bit BUT I had nightmares debugging stuff until I protected my data in this way as variables were changing underneath me in tweens etc., and I couldn't tell where things were being accessed... Making an effort to encapsulate your data in classes and global tables really leads you towards better design decisions and more elegant code... Code Duplication Avoid like the plague! Any time you find yourself copying and pasting code, make it a utility function in a global object! Object Creation Minimisation I'm on less certain ground here, but as a general good practice, minimize the number of objects you create, e.g. store an image once using readImage(), check for object creation in loops that could be moved outside etc. Its easy to write a Cache class that does on-demand loading and caching for different objects so you only ever create a named 'thing' once...Shout if you want the implementation... Be interested in all your thoughts, Brookesi