Today we’re going to go into the weeds and a bit of coding theory, since I am currently in the process of discovery. Mainly, I’ve been trying to figure out why Paper Zeppelin has a tendency to run really slowly after about 5 minutes of gameplay…sometimes. Other times it doesn’t and cracks right along in a jaunty and, if you forgive the pun, spritely fashion. To put it as diplomatically as possible, this is bad. Worse, I don’t quite know why, which has led to some tests, which has led to some confusion, which will hopefully lead to some sort of “a ha!” moment and spiritual catharsis.
To start, we need to consider the concept of the Sprite List. Now, for the purposes of our discussion a Sprite List is a list object that holds and indexes sprite data. You can put anything on a sprite list, as long as it is of the class type “Sprite.” That means that enemies are can go on the same list as players, and ground and whatever else I want. Now, in Paper Zeppelin different types of things are stored on different lists for reasons that I won’t get into now.
The main magic of the Paper Engine runs on looping through all of the members in that list and drawing them on the screen or telling them to update their robot chicken code or whatever. The system will also run through the lists to do comparisons for interactions, and in the update and interaction code it will occasionally call code to remove a member from a list.
The main magic of the Paper Engine runs on looping through all of the members in that list and drawing them on the screen or telling them to update their robot chicken code or whatever. The system will also run through the lists to do comparisons for interactions, and in the update and interaction code it will occasionally call code to remove a member from a list. It’s the removal that I find might be an issue.
A quick aside on what I mean by “member.” I may be using the term incorrectly, but in this case I mean a object that is on a particular list. So if there is an enemy on the enemy list, that enemy is a member of the Enemy List. “Member” is also used to refer (more correctly) to class objects that are part of a Class. So an Dive Bomber is a member of the Dive Bomber class, and also a member of the Sprite Class.
This gets up to the titles (Yay!), which is to say that I don’t think that removing the member from a list, actually removes all of it from memory. I mean, looking at the memory and CPU usage it seems like it does most of it. Heck, even the total number of members in the list changes (I have a subscreen that shows me data like that). But I’m not sure if it actually does, remove it all from memory. So I found a method where you can set a member to null, which basically resets the total values of the object to less than zero. I say less than zero because zero takes up space. “Null” manes that there is nothing there at all. In math terms it’s called an “empty set.” Plus, if there are null value objects that aren’t connected to anything anymore (like the newly nulled objects) C# has a garbage collection protocol which removes it from memory like it was a political dissident.
After setting it up it, I don’t know, seemed to work? But then it started to slow down anyway. It’s possible that I missed something. Likely even. Or it’s possible that my system was doing an update to Microsoft Edge in the background and doesn’t know how to prioritize. It’s hard to tell.
What’s also hard to tell is why something is working at all. Here’s some code to show you all what I see:
Sprite current = enemyList[i];
Right then, let’s start with this up front – this works. This is close to pseudocode, but it’s also syntactically identical to what is inside Paper Zeppelin. Let’s walk through the lines and then discuss how what is happening here doesn’t actually make any sense to me, unless it’s doing something rather clever. Shall we?
All of this lives inside a loop, and as part of that loop we can declare what are called local variables. These are often used for things that are only used inside the loop itself. The counter (like “i” in the above case) are usually local variables. What I’ve established here is that we are creating a sprite variable called “current” and we are assigning it the values of the sprite that lives at wherever “i” is on the enemyList. When you do this, the variable is no longer connected to the original value. So in this case, “current” should not be connected to the sprite in enemyList[i] anymore. This is important.
The 2nd line is where this starts being odd. Now, since “current” has taken the properties of the enemyList instance, that means it also has an Update() subfunction, which we can call. That’s what this line does. What is less clear is why it works. I mean, it should work once, but if the update code moved the sprite, then the next time we burn through the list and get to enemyList[i] the new position will be saved there. It’s spooky action at a distance.
Finally, the last part removes a member from the enemyList. What’s interesting here is that somehow, the system once again knows which member “current” is referring to. I can go in and tell it : Remove.enemyList[i], and that works*.
*It does not. Removing a member like that causes an error since the for loop as constructed uses the total member count to determine how many times to run the loop. But it does remove the list member, which is something.
Of note, if I make “current” to be null before I remove it from the list, I get a compiler error because the system can no longer find enemyList[i].
All of this leads me to believe that I am dealing with something altogether new and interesting. Speaking of “new” if I instead invoked this syntax instead : Sprite current = New sprite, I think that it would behave the way that I expect it to. That means that what we are dealing with here is something like a handle. That means that the local variable that I am calling is actually just a shorthanded way of referring to enemyList[i] or whatever else.
That must also mean, that when I set “current” to null, then it is doing the job to the list as well. Which is unfortunate. Because I started down this weird rabbit hole trying to figure out where my memory was leaking out onto the floor, and the above code isn’t the culprit. Well, at least I learned something if something else.
Oh yeah, formatting! I don’t know if I will ever do it again (this is a development blog, not an online magazine) but for now, let’s enjoy my aside column and not think too hard about how it probably looks really, really broken on mobile devices.