Tuesday, January 29, 2013

Plugging Memory Leaks in Corona


I’m finding out that Lua is not as forgiving a language as I had first thought.  These were the changes I made in level0 to get rid of the various memory leaks I had created.
Destroying Objects
This was the most obvious thing to do, and so became the first order of business.  We have been inserting objects into a self.view group – and that order of removal is a very important thing in Lua.
Using this article on removing objects properly as a guide I found a number of things.  First just removing an object from a group isn’t enough for the garbage collector – the reference the variable is pointing to must also get removed.  So there are two steps to destroying an object – first is to remove it from a group by calling the group:remove( object name or index ) or object:removeSelf() methods.  Then you have to set the object = nil to remove the reference.
But wait – there’s more!  The order of removal from a group is also important.  In fact a common pitfall is to iterate thru a group in a normal forware fashion – which may in fact skip every other item in the group.  I’m going to copy and paste the relevant section from the above link here:
Common Pitfalls
A common mistake is to improperly remove all objects from a group. This typically happens when you write code that iterates through a group attempting to remove each child from the display hierarchy. It’s natural to iterate through the group in the forward direction. However, this can cause no end of confusion.
Continuing with our solar system example, consider the following where we attempt (incorrectly) to remove all the objects from the solar system.
for i=1,solarSystem.numChildren do
        local child = solarSystem[i]
        child.parent:remove( child )
end
The problem here is that we are modifying a collection (i.e. the group’s children array) as we iterate through that same collection. The result is we remove every other child. The easiest way to illustrate this is with a parallel example involving an array of integers:
local array = {1,2,3,4,5,6,7,8,9,10}
print( table.concat( array, " " ) ) --> 1 2 3 4 5 6 7 8 9 10
 
for i=1,#array do
        table.remove( array, i )
end
 
print( table.concat( array, " " ) ) --> 2 4 6 8 10
The fix is to iterate backwards.
for i=solarSystem.numChildren,1,-1 do
        local child = solarSystem[i]
        child.parent:remove( child )
end
Of course, this only ensures that all children have been removed from the display hierarchy; you till have to set all references to these display objects to nil. So in this example, we were merely trying to illustrate the highlight the pitfalls of iterating forward through the children of a group and modifying the group at the same time. A good implementation would also set the corresponding properties in solarSystem to nil for proper cleanup. 
So as part of my exitScene function I’ve added the following:
for i=screenGroup.numChildren,1,-1 do
        local child = screenGroup[i]
        child.parent:remove( child )
end 
Then I do this:
screenGroup:removeSelf()
And then set the various objects equal to nil.
We aren’t done yet tho…

Forward Referencing

This part comes from reading this handy article on The Significance of Local Variables in Lua.  We’ve all seen this:
local myFunction = function()
        callback()  -- ERROR: function 'callback' does not exist yet
end 
local callback()
        print( "Hello World" )
end 
myFunction()    -- will produce error 
Refactored this way, the code works: 
local callback    -- forward reference (for later use)
local myFunction = function()
        callback()    -- output: "Hello World"
end
-- function below is still local, because of forward reference
callback = function()
        print( "Hello World" )
end
 
myFunction()    -- no error; happy code :-) 
So I took every function that was not part of the scene template and moved it above function scene:createScene( event ).  This took care of a few ambiguous runtime errors.  This includes the addListeners() and removeListeners functions.
At this point things were supposed to be better, but I started to get screen freezes – holy cow things are getting worse!

Runtime Listeners

I next turned to this handy guide, Corona SDK Memory Leak Prevention 101.  And found this handy scrap if information:
When you remove a display object, listeners that are attached to it are also freed from memory. However, when you add listeners to Runtime (e.g. enterFrame listeners), those never get freed until you manually remove them.
 
A common leak that occurs with Runtime/enterFrame listeners is when a developer adds a Runtime listener to the particular screen they’re in, but they forget to remove the event listener whenever the user leaves the screen. So what happens is, the user leaves the screen, and then when they come back to it, there are two identical Runtime listeners running on top of one another.
Now, we do add a Runtime enterFrame listener with the gameloop code.  I make extra sure that the removeListeners function has the Runtime:removeEventListener('enterFrame', gameloop ) and that it gets called.

Timers and Transitions

Again back to this handy guide, Corona SDK Memory Leak Prevention 101.  I’m just going to copy and paste from the guide:
Timers and Transitions are probably one of the most common causes of crashes, mostly due to the fact that countless (unaccounted for) things could happen between the time the timer/transition starts, and the time it ends. What’s worse, if you forget to cancel these and nil out their variables when no longer needed, things could get stuck in memory and cause more leaks.
One way you could manage these is to store ALL of your timers and transitions in a table, so that when you know for sure no timers or transitions should be running, you can cancel all of them at once.
If you add the following code to your main.lua file (or another module and require it in), you can easily keep track of your timers/transitions and also cancel them all at once if and when needed:
timerStash = {}
transitionStash = {} 
function cancelAllTimers()
    local k, v
    for k,v in pairs(timerStash) do
        timer.cancel( v )
        v = nil; k = nil
    end
 
    timerStash = nil
    timerStash = {}
end
--
function cancelAllTransitions()
    local k, v
    for k,v in pairs(transitionStash) do
        transition.cancel( v )
        v = nil; k = nil
    end 
    transitionStash = nil
    transitionStash = {}
end
Then, whenever you start a new timer or transition, you assign it to the respective table first, like so:
timerStash.newTimer = timer.performWithDelay( ...
transitionStash.newTransition = transition.to( myObject { ... 
Then you can use the cancelAllTimers() and cancelAllTransitions() functions to stop all timers and transitions all at once. If you have timers you don’t want to be cancelled, then don’t add them to the “stash” tables in the first place (but be sure not to forget to keep track of them). 
So I added the above cancel functions to level0, and then call them after the removeListeners call.  I found I could do this in the gameloop function and it would work:
transitionStash.newTransition1 = bg1:translate(0,2)
transitionStash.newTransition2 = bg2:translate(0,2)
 
Things are better but I still was getting some runtime errors – not as many, and no crashes but still annoying.

Proper Placement of Cleanup Code

I was experimenting with the right place to put all this garbage collection code – and in desperation decided to go back to the documentation.  One last link – storyboard.gotoScene():
Description:
Used to transition to a specific scene (determined by scene's module name). Internally, this will handle all loading/unloading of the scenes. Scenes may also be explicitly unloaded/removed by the user.
When this function is called, the "exitScene" event will be dispatched for the currently loaded scene (if one exists), followed by the "createScene" event (for the specified scene—sceneName—if no "view" property is present), and then the "enterScene" event (also for the specified scene—sceneName).
It dawned on me – keep the code in the exitScene and it would always get called in correct order. 
At this point I was about 99.5% there.

Zombie Scenes

Yes, the dead still inhabited the program.  I was seeing calls to level0 when in level2 – no crashes but still getting some runtime errors.  So I found this reference to storyboard.purgeAll().  In cutscene1, the scene:createScene( event )function I replaced the call to storyboard.purgeScene( priorScene ) with storyboard.purgeAll().  Inside the Lua command box I was seeing calls to scene:destroyScene( event ) in both level0 AND train3.  More importantly it got rid of the last runtime errors.

Sunday, January 27, 2013

The stupid bug

I think this is a Corona/Lua problem - and we stumbled into it.
So for the 'static' scenes we seem to be okay. It's the ones that have a scrolling background that have problems.
At about 25 to 29 seconds - when objects are not getting inserted such as enemies - I get the same problem. A run time error thrown by Corona/Lua:
Runtime error
...pos\flights of fancy branch\Managers\moveManager.lua:29: attempt to call method 'translate' (a nil value)
stack traceback:
[C]: in function 'translate'
...pos\flights of fancy branch\Managers\moveManager.lua:29: in function 'moveBackgrRuntime error
...pos\flights of fancy branch\Managers\moveManager.lua:29: attempt to call method 'translate' (a nil value)
Same thing happens whether or not the code to move the background is in John's new module or not.
I think this this frankly is a Lua problem and it has to do with memory management. Is this a big issue? Not sure. What happens when a player has to deal with a phone call, or real life issue and walks away. So I think this is an issue - we probably need some kind of global answer where after 20 seconds of no user input so wait state is put in place.
So that is problem 2. Problem 1 was why my intro/train code wasn't playing well with John's changes. And I stated that correctly - John did the right thing in terms of pushing common tasks into a single module, and my code wasn't playing well with his.
Oh boy - Lua is twitchy.
I did make some changes to some code - I need to review before I push it up to SVN. In a nutshell tho - take a look at how I load the objects into memory in level0. Very old school procedural code way of doing things. In essence - pay attention to '-- BEGINNING OF YOUR IMPLEMENTATION'. It seems to be that I need to load objects I'm going to manipulate as well as any other functions into memory BEFORE the scene management code gets called. Ouch. Pain. Not happy. Very very old crappy yecch bad. Double plus not happy. But by doing that I was able to get to level 3 - the horizontal planes flying in formation - before stuff crapped out.
I also made sure I not only killed - I added rings of garlic and then sowed salt into the ground - in level0 regarding the objects called and created. Can I say that I am not a fan of garbage collectors I can't touch. C++ is a happy place. C is good. C# is meh - and Java is in the same place. Oh well, I am a dinosaur and the tar pits beckon.
In any event I am going to do more review etc. of the program before I post anything. John - I only added one function and also defined bg1 & bg2 as local vars in Managers.playerManager. I think that is kinda benign - okay so I added a third arm to the guy but hey, it seemed like the right thing to do.  It's not like I added another head or anything like that...






Saturday, January 26, 2013

TBowers Week 3 Report

This week I spent working on the two main feature/bugs I had assigned to myself in Trello. The first was an issue where restarting the game following death would lock up. Debugging this issue helped me understand a little more about the object lifestyle in Lua and how that interacts (and conflict) with the storyboarding framework.

The storyboarding API gives us a clean interface to create, enter, exit, and ultimately destroy scenes. It allows the game to float between scenes easily which is how we transition between cut scenes, the gameplay levels, pause screen, main menu, etc. However, in each of these files we have to be more careful about the things we declare (and initilize) globally.

e.g. (at the top of the file)
local player = playerManager.addPlayer()

This is appears to be called once the first time the file is loaded, and then never again. So when the player died and went to start again, the scene:createScene for the first level would be called with a player object that was nil.

The solution was to declare the player variable globally (e..g local player = nil), and then in the create scene function, initialize it. This way, the second time through the scene (in the case of a restart), the player object is still initialized.

The second issue I tracked was why the player health bar wasn't showing up correctly in all the levels. John did some great work to encapsulate our player behavior into a player object that persists through the levels. This player object holds the players health. This was conflicting a little with the creation (and deminishing) of the player health bar. So I encapsulated the health bar inside the player object. Then, for each level that has the player object support in it, all we have to do is call a takeHit method which will apply the damage and update the bar.

Sometimes it feels like we have an insurmountable amount of work left but we keep chipping away and the product keeps getting better! Up next for me is going to be testing and trying to integrate LunaTest into our project.




Sunday, January 20, 2013

A New Quarter


As a group, we have now discussed and outlined our various milestones for this next phase of our game development. I think we all have a pretty good idea what we expect to be able to accomplish during this quarter and it should prove to be quite exciting. In my opinion, we collaborated very well as a group last quarter. Although we each had "official" assigned roles, we all contributed to the project in a variety of ways, with each of us taking on the roles of developers, testers and integrators as needed. I expect that will be the case this quarter as well, and I look forward to seeing what further progress we will make on the game.

Something I noticed recently (and we have discussed as a group) is that the founder of Corona seems to left the company and started up another game development platform. The website is lanica.co. One of the products described there is Platino, "a cross-platform (iOS and Android) native game engine built for Appcelerator Titanium". Hm, sounds familiar. I will be very interested to see what the future holds for this platform in comparison/contrast to Corona. This underscores the fact that the relatively new industry of mobile app development continues to be an exciting and rapidly changing one. Nonetheless, even if we were starting the project right now, Corona would still be the way to go since from everything I can tell it is still one of the most popular and robust platforms, with a seemingly strong community of developers making use of it.

Getting a game plan for Q2


I’ve added the following as my individual focus items for the term:
·         Add other enemies – add a couple more enemy classes to the game.
·         Player achievements – based on these player events:
o   Total score
o   Total score for specific enemy types
o   Some other kind of event
·         Player levels – based on score.
·         Power ups – two basic types:
o   Found in the environment
o   Abilities, skills that get ‘unlocked’ based on player score and level.
Based on the above, I do have a built in hierarchy/dependency tree:
·         Player score breaks down into:
o   Total Score
o   Total Score by specific enemy types
·         Player level which is based on Total Score
·         Power ups which also have a dependency on player score and player level.
Okay, so now I can do some planning.  Looks like for now the work is going to be done in the back end:
1.       Add more functionality to the playerScore, playState modules that will take care of incrementing both total score and total score by enemy types. 

2.       Make a decision as to keeping with the current JSON tables, or start making more use of SQLite to hold onto this data.

3.       Once the scoring is taken care of, achievements will have to be addressed.  Currently achievements are based solely on total score – what gets delivered is some text based on a set of ranges.  If the players score falls between certain ranges then what gets displayed is the text for that range.   That’s fine for now, but the next steps will need to be saving player achievements – probably in the SQLite database.  This way calculations/algorithms only get hit once per achievement.

Back in Business

Well,  We're back from our break (actually we're 2 weeks in now) and we are running "full steam ahead" to push out as a high a quality product as time will allow.

This week I did some code cleanup, brought up a need for our team coding standard, and had the usual team meetings.  I also went over the product features and began analysis to determine feasibility for the Q2 plan (these will be finalized as a team).

Time was also spent blogging this, editing and contributing to the team Q2 plan and writing an individual Q2 plan.

Thursday, January 17, 2013

TBowers Week 2 Report

Boy that went fast! Can't believe we are already in January and into the second quarter of the software studio. This past week I spent partially restoring functionality to our subversion server and catch up on tagging. In addition we have a great new custom tool for focused testing of features provided by James that was moved into its own branch for everyone to use.

I reviewed what we currently have on the docket in Trello, assigned myself to a player health bug where the meter doesn't show or function in all of the levels. This is the kind of thing we have to worry about when scaling out our game to the total number of levels and features we plan on delivering. Hopefully common elements such as the HUD, pause screen, etc. can abstracted away enough to be easily usable.

I've also assigned myself to a new feature to allow continuation of gameplay after game end. I am still investigating just where and how to integrate that feature. Hopefully with the storyboard framework that is in place, it won't be all that difficult.

Finally, I am working on my individual goals for the quarter as well as helping the team draft our team goals. There is a fair amount of work to do but after our success last quarter, I don't foresee the challenges being anything we cannot handle.

Sunday, January 13, 2013

Intro and Training and Player Choice Oh My!

It's been a busy intersession.

Got the intro, some player training and the game hook - the player making a choice - done and sent to SVN.

It was a good trip to take with Corona - learned a number of things along the way.  Corona is good, as in classic VB was good - anyone can do stuff but a skilled programmer could work wonders; but it's the newbies that outnumber the skilled programmers by a lot.  That's the classic double edged sword - make it easy so the masses can get into it and do stuff - but if they don't learn and too much crud gets out into the wild, then it's the tool, not the meh programmer who takes the hit.  Make the learning curve steep like C or C++, get a cadre of professionals but then the masses heads to Java (or .NET).

Anyway the into has action, some animation and sound, buttons to push, and the CHOICE.  Not capitalized on yet, but it's there and we'll see what happens as the quarter goes on.

Code was put in place so that the intro was only played once.  But I added some tap events to the title code; tap on the main graphic twice and you can redo the intro.

I think we can do more with this, add a menu of options etc.  So it's a good thing to have the event there, and we can capitalize on it later.