Menu

A ReferenceCounted List

April 12, 2012 - C#, Programming

BASeBlock – Music Manipulation Lessons

As with most games, BASeBlock has music. Originally, I implemented a naive approach to have “Multiple music” playing; a simple stack. The multiple music idea is sort of like
how games might change the music to a boss music when a boss appears, and change it back when they die, or how a certain powerup might change the music while you have it.

This implementation sat for a while. it used a stack based approach- the Sound manager had PushMusic and PopMusic methods.

However, several critical flaws in this approach became clear after I added an invulnerability powerup. Everything seemed to work fine, (get the powerup, you’re invulnerable and while you are there is different music) however, the problem became clear when I, while still invulnerable, released a boss. The boss music would start playing; however, while the boss was alive, the invincible power would run out; it would “PopMusic” which would revert the music from the boss music to the starman music, and then only when the boss died would it go back to normal. This is obviously not intended. The ideal case would be:

  1. Player starts level. Level Music is playing.
  2. Player gets invulnerability powerup; invincible powerup music plays.
  3. While invincible, the player, or something causes a boss to spawn.
  4. when the boss is spawned, the invulnerable music can continue until the power runs out; at which point it plays the boss music, or, the boss music can replace the invincible music. The former is probably overall a better idea.
  5. Either way: the music must fit. Invincible music should only play while the player has said powerup; and boss music should only play when there is a boss.

Obviously, my approach failed miserably; it worked fine, but I had only had a single “active” piece of music at a time; how do you manage multiples?

After some thought, I considered the idea of “reference counting” or keeping track of how many times a given piece of music was requested to play. a boss spawning would increment the boss music by one, a second one with the same music would make it two; each time this happens, the sound manager could re-evaluate which piece of music to play based on finding the maximum reference count.

With this idea, I rearchitected some of the code within the SoundManager. The SoundManager (technically cNewSoundManager, since it was a rewrite of a strongly coupled version I had before) is essentially a class that, well, manages sound and music. I have a interface class that allows for different actual implementations of the details of playing sound (“Driver” classes, of you will) The Manager class itself merely deals with the details based on that basic functionality, which exposes a few critical events, such as music stopping and whatnot. The original “PushMusic” and “PopMusic” stack based approach used a small data class, shown here:

A minor explanation may be necessary; iActiveSoundObject is an interface class that is implemented by the “driver”; same for iSoundSourceObject; the details of how they work isn’t important, just that their interface methods do what the interface definition says. A Active Sound object is something that is “active” usually, this means it is playing, but it could also be paused. A Sound Source object can be used to “spawn” Active Sound Objects; in order to actually play music or sound, a iActiveSoundSource object is required. Rather than discard this class I extended from it. Arguably, I could have simply changed the actual class itself but that could always be done later:

Again, another private class. The Implementation of IComparable is vestigial from when I was flailing around trying to shoehorn the old stack-based approach into the new reference counted method using a SortedDictionary. Then I realized it was stupid and just made the data structure a normal dictionary.

Dictionary<String,TemporaryMusicData> to be precise; This indexes the TemporaryMusicData instances by Name (Key); the Name/Key is used by the sound Manager to index Sound sources, so getting the appropriate source is easy given a name, and it’s guaranteed to be unique since the listing is taken from the file system itself, and the loading routine has other considerations to prevent duplicate entries (and error handling for duplicate key Exceptions if they do occur). The Occurences field is basically the entire purpose here; when “Temporary” music is told to play, it merely increments the field for the appropriate entry at the Named Index; then both the Stop and Play routines will call another routine that Ensures that the item with the maximum occurences is playing. The implementation for the relevant routines:

So far, this has worked well.

However, more recently I found that I also need the same sort of “reference count” management for other things related to powerups, such as the “DrawAttributes” of various objects. But it would be foolish to clutter up that code with this sort of thing. Surely there is some way that I can add the feature with little to no changes to existing code? Turns out, that leveraging a few C# features, this is relatively easily accomplished.

Consider the Nullable<T> class. Any struct or value type can be made “Nullable” using it; there is even a shortcut in the language syntax for type definitions to use it, by appending a question mark, (Nullable is equivalent to int?). What we want is a way to- generically- make a class “reference counted” so that only the value that has the highest reference count “is” the value. The Nullable<T> type can be implicitly cast to type T in most contexts; so you can change a T to a Nullable<T> type with few code changes, which is what we are after.

Enter ReferenceCounted<T>

ReferenceCounted<T> is the name of the class that I created (or, as I write this, am creating) for this purpose. My original idea was to use implicit cast operators to make it a simple type change; assignments to the object of the “old type” (type T) would “automatically” be added to the reference list; going the other way, the ReferenceCounted<T> Type would be implicitly cast to T by way of taking the T value it currently has with the highest reference Count. This hit a snag, however; the second cast, thankfully, would work fine, but the first would not have the proper information; the cast operator is a static routine and wouldn’t have access to the ReferenceCounted<T> Object that is being assigned.

somewhat miffed but not surprised (it would be silly to provide for overloading of the assignment operator, but in this case I wish there was an exception), I didn’t give up; I just thought about it a little. And it hit me- I don’t need to overload the assignment operator to overload assignment; I could overload the addition operator and implement the “assignment” code there; this is what the Event classes do for event hooking; and I could use -= to remove “references”. Arguably, this would take more code and wouldn’t be quite as clean as I was hoping, but for the most part the actual reference counting logic would be out of the way, handled mostly by the implicit cast to T.

After some effort… it was made. Here is the source code:

Aftermath

Pleased I had created a nice implementation, I set about creating the Comparison routine. Unfortunately, to my horror and surprise, the class which I wanted to use in conjunction with this class in one instance, ImageAttributes, had no way of getting it’s ColorMatrix. This presented an issue since I didn’t want added ColorMatrix values to mess about with the image, and the results could be less than extraordinary unless I cached each ImageAttributes.

And that was the entire purpose. However I decided to consider how else to acheive my goal; the goal here was to prevent powerups from changing the state of GameObject’s appearance in a manner that prevented them from undoing it. So, for example, powerups might have a limited duration, and the results from a overlap of two powerups could result in a confusing ending state for the object. The idea was to replace the GameObject class’s “DrawAttributes” field with a ReferenceCounted; then in it’s draw routine, it would assign use that in the appropriate method which would implicitly cast it to the maximum referenced item in the list. All I needed was a way to compare DrawAttributes aside from as references; but the ImageAttributes class, sadly, does not provide this functionality.

So how do I address this?

I considered possibilities, and the problem, a bit more thoughtfully. Evidently, the ReferenceCounted<T> class would be very useful for it, but what would I use it for.

I’ve decided- though not yet attempted to implement- that I would use the ReferenceCounted class to keep track of The powerups themselves rather than a few fields of the gameobject. Since the powerup classes are what would result in the unwanted behaviour, it makes sense. So how does it work? Well, the framework basically allows a GameCharacter to have a list of GameCharacterAbilities; the GameCharacter calls the draw function of each when it draws, and it calls a frame function when it’s own frame function is called. My idea is to change that to a ReferenceCounted<GameCharacterAbility>. The code could then be changed to only call PerformFrame and Draw for the one with the highest reference count, or something similar.

The other possibility is to change the GameObject’s DrawAttributes field to a read-only property that is created “on the fly” from another new ColorMatrix field; the ColorMatrix item could be a ReferenceCounted object and therefore the use of that object in the property would use the implicit conversion operator. I’m trying to avoid this, even though I cannot foresee a circumstance where the ImageAttributes class provides something that I can’t do with a ColorMatrix (oh it does, but nothing I know how to do) I prefer to keep all my roads open, so to speak. If there was a way to compare the innards of the ImageAttributes, I could just change the DrawAttributes field to a ReferenceCounted object and make a new comparer, but it’s unfortunately not that simple.

Have something to say about this post? Comment!