Reflex – Dev Diary 55
Complex positional nested for-next statements make my head hurt. But that’s what was needed to fully optimise the automatic generation of the shadow caster wall segments.
Originally my approach for the shadow casters was to have the code scan through the wall tiles when the level is started and to create invisible objects to describe the walls for the lighting engine. Each wall piece would use 1 or 2 invisible objects; for example a straight piece of wall would need 1 object, a corner would need 2, and so on.
This method worked perfectly. And then I got to the levels in world 4 and 5 and those get large and complicated and although the walls were being generated absolutely fine there was a noticable performance hit as the lighting engine has to iterate through all the wall objects for every frame of the game.
So I had a think and came up with 2 potential solutions – in the end I used both because they equally good.
First up. Would it be possible to ‘buffer’ the vertex information used for the shadows so that it didn’t have to be rescanned every frame? This was the first time I started digging into how GameMaker Studio handles the vertex buffers so it was a bit of an experiment. The game mainly uses static shadows because walls don’t move (who’d have thought it?), but there are a few dynamic shadows that I wanted to keep. For example, the destructable walls and toggle doors bleed light when they are opening. So, I needed to test and see if I could pass more than one vertex buffer to the shadow shader. I was very pleased to find that I could. So with this knowledge I moved the static wall vertex buffer to the ‘room start’ process so that it is created once, and then left the dynamic walls to the ‘step’ function. This would reduce the number of calculated walls per frame massively.
Second. Could I rewrite the wall generation code so that the invisible shadow casters spanned multiple tiles? This was going to be quite complicated and my initial method of doing so was not working. I had thought that I could leave the wall generation as is (i.e. let it create the hundreds of single tile objects) and then iterate through each of the straight pieces and see if they are overlapping with another one next to them. If they were then the second one was deleted and the original could be extended to span the gap. Repeat this for each shadow caster and you should end up with a reduced wall count. That was the plan. But it just wouldn’t work. The routine I wrote seemed to get confused as to what objects it had dealt with – I believe it’s because I was working with a for loop and was removing elements from the same loop. I even tried creating an array of objects that could be deleted after the fact but that wasn’t working either.
In the end I went back to the drawing board and re-wrote the entire shadow map code. This time I had success. The code would check to see of the next piece (x++) was the a continuation of the wall before it and if it was extend the piece, otherwise start a new piece.
The final result reduced the number of wall pieces from one map from above 600 to less than 30. Amazing.
As this took me most of the week to sort out other additions and tweaks had to wait, but I have spent a few hours on the intro sequence. Initially I was coding the animation but getting it to reposition and scale for mobile phones and tablets was proving to be a pain so I decided to build it as a ‘sequence’ instead (the GMS2.3 animation object). This has made certain things a lot easier, such as being able to visually place sprites and animate them on a timeline, but it does make a few things harder.