Let me explain my problem. When i code complex modules in Codea (5+ tabs, thousands of lines, many touch, draw, save, buttons, data functionnalities) i need to implement everything down to the end to get an idea of what i will really be able to do and what will be the limits (my limits, lua limits, fps limits, ipad memory limits). At that stage i have a prototype working, and i know how to do every little technical step “the best way”, at least from my perspective. But the code is messy, because at the beginning i didnt really know how i would do things, so it is a succession of add-ons.
So i start a rewrite of everything in nice classes, simplifying functions and fields. But i try to proceed from the existing code, to test my redesigned functionnalities step by step with the existing environment. That works fine for the low level functions, but as i reach intermediate and top level functions, where the new functions interact with old design functions, i get more and more bugs, the cause of which is more and more complex to visualize, because everything is so intricated.
I find that adding complex functionnalities on top of already written lua function is surprisingly easy an fun, but reworking intermediate functions is difficult and no fun at all. How do you guys do it? Do you restart from scratch each time you rewrite the program? Is there a way to do it that keeps it easy and fun? Any advice welcome!
.@Jmv38 is it possible to offload as much of your code as possible into isolated “library” projects that you can include via the Project Dependencies option? If your library code becomes well-tested and provides a stable and predictable API, it could help you to have less code in your actual prototypes.
Regarding intermediate code — by that I assume you mean “glue” code. The stuff that connects components together? There is no easy way to avoid that, but with time you might find that common design patterns emerge that let you handle it in a consistent manner.
Thank you @Simeon. Anyone else having some experience on this subject?
@Jmv38 - This was a pretty reassuring post as I’m going through exactly the same issues!
I started learning Codea about this time last year - was a Lua noob, but had a reasonably strong programming background (Python, C/C++) but not much real-time game programming stuff (except game coding i did as a kid many moons ago!).
I managed to prototype a few games and ‘tests’ in Codea and obviously as i learned its capabilities replaced my long winded ‘noob ways’ with more efficient methods of achieving the same thing!
So been developing something for a number of months now that has gone through this ‘refactoring’ process and now on rewrite #4 and have exactly the same issue of having to keep an eye out for new bugs as I update the code. Once tested, I tend then to migrate these to a ‘library’ (as @Simeons suggestion) with predictable entry and exit points and try not to touch it again once I’ve tested it extensively.
However, the larger my code-base gets with multiple tabs etc (now > 25!) I also struggle with keeping track of how these things interact with each other as my code gets more complex. I increasingly try and ‘layer’ my code so that its effect gets more and more granular through careful use of functions and classes as I break down the problem which then becomes easier to incrementally test.
The other useful tip is to try and decouple ‘behaviour’ or ‘game logic’ from the ‘draw’ or ‘graphics’ display code. This can really help, as I find things quickly become a bit of a mess otherwise.
Ideally, I’d love to see some more advanced debugging options in Codea (e.g. Watchpoints etc) - I find I can just about get by with using ‘watch’ or printing values to the console, but it quickly gets cumbersome when you need to keep tabs on multiple values within a class etc. - a small but useful point.
I guess (from my Uni days!) there’s often no substitute for ‘top-down’ design ( or whatever it’s called these days) and working stuff out on paper first - I think most of the knots I get into is often being lazy about working out the structure of things before I sit down and start coding - maybe this is the curse of the iPad!
Ok, i have started the rewriting, here are the principle i’ll try to stick to. I dont know if they are really ‘the’ good ones, i’ll probably make some changes to theses rules as i go on, or add some more rules if i feel i need them. So far it feels right,
--[[ the principles i try to follow for this rewriting are: 1/ splitting the code of some huge class into smaller classes it has been a real relief! >There is less code per class, so it become more clear. >I see many unecessary sublevels i can remove. >And it makes some missing elements become obvious: they are needed for simple calls from the outside of the class (methods or data fields). >Also i can see a nice 'russian dolls' structure appearing (or onion-like): i can rebuilt a series of simple, clear, robust and verified levels on top of each other (it will be 5 or 6 levels in the end) > The drawback: it is going to take me more time than i expected... but hopefully less headache! 2/ a 3 levels structure emerges naturally within a class: -- ################### interface (or high level) functions ############################ -- these function are designed to be called by higher level objects using this class -- these function should be clear and not change or depend on the implementation details -- they should call only internal functions and use self.xxx parameters -- ################### internal (or mid level) functions ############################ -- these function use and set self.xxx parameters, calling the low level functions -- they do the job, but should not be called from the outside of the class -- they mamage the complexity of interactions -- ##################### service (or low level) functions ############################ -- these function should be passed explicitly all their arguments, and return -- all the variables then create or modify (to keep their impact visible) -- they do not read or write directly to the class fields (except for complex objects -- like a mesh ms for instance, but they dont know it is a class field) -- they are 100% autonomous => easy maintenance 3/ in the init(), i split the fields in 2 categories (maybe in the methods too): -- static parameters -- dynamic parameters (behavior) 4/ with these huges changes the code will be cleaner in the end, but it implies i have to restart more or less from scratch, keeping an eye on my previous implementation to guide me. --]]
Here’s a few things I’ve done with my library code that may (or may not) be useful:
Implemented an “import” method so that in a program I only load in the parts that I want (everything still has to be read, but only the bits that I want are loaded and the rest is soon garbage collected). This also makes it easier for one library tab to force the import of another meaning that I don’t have to remember all of the dependencies.
My library project is the test suite. Since the
Main.luatab doesn’t get imported, I can safely put test code in there.
Each library has/ought to have its own test code. When I run the library project then I can run the testsuite for each tab. This doesn’t necessarily work for everything, and I’ve only implemented it on some of the more fundamental code (quaternions, matrices, that sort of thing).
Thanks for the advice @Andrew_Stacey. I agree testing is a major point, and in my current rework i am including test functions in the class itself (or maybe i should make a sideclass “myclass_test” to gather those tests). I also think about including an interface for quick testing all relevant parameters, with the new parameter functionnalities.
Thanks for the advice @Andymac3d. I thought of splitting the code into draw/ touch/ data/ save/ parts, and low/intermediate/high level functions, but maybe your behaviour/gamelogic/graphics split is clearer. I’ll give it a try.