When I first started developing BASeBlock, the basic idea was just to get a feel for how Animation, rendering, and other concepts were dealt with in C#, as opposed to the aging language I was familiar with VB6. Additionally, I wanted to make use of Threading. After getting through the initial hurdles and issues related to the type of code I would need to write with BCDodger, I had a good idea the type of things I needed for BASeBlock.
BASeBlock’s actual Game information is, not surprisingly, kept inside a instance of a “BCBlockGameState” object. There are a few folly’s in my design, whereby some elements that would probably be best placed as members of BCBlockGameState are in fact members of the main form, but everything works well as it is, and if necessary objects can get a reference to the form itself using the BCBlockGameState ClientObject property, which is a interface that defines the type of stuff that is more implementation specific than ought to be in the State instance. Rendering is managed entirely within the Paint event of the main form’s PictureBox- (and likewise for the Editor). Rendering and the actual GameProc() routine (that makes things happen) are similar; they both iterate through the various objects in the game and call methods. The GameProc() routine animates balls, GameObjects, Particles, and AnimatedBlocks; the Paint event basically iterates through all those objects and get’s them to paint on a bitmap which is them blitted to the PictureBox when necessary. Of course having these two activities on separate threads meant that I tripped on a few things while I was getting started- not locking this variable, trying to call methods of the form from within the game loop, and other similar contention issues. All of those have been resolved, to the best of my knowledge.
Another goal while working on BASeBlock was to make it easier for the creation of new blocks. The way the older Poing game (VB6) did it was to only have a single Block object, which had an Enumeration that determined what kind of block it really was. This had the issue whereby the Block had a lot of fields that were only applicable with certain block types, and weren’t applicable at all otherwise. This was a primarily a result of the fact that VB6 isn’t really an Object Oriented Language, but rather an Object-Based Language; while you can fake Implementation Inheritance using the Implements keyword and an aggregate class, it’s a lot of boilerplate that I couldn’t be bothered with for little gain. With C#, and more importantly proper OOP constructs, I could try my hand at a more reasonable Object heirarchy.
Collections
By far one of the most annoying things to deal with in VB6 is collections. they are finicky, and more importantly, the built-in Collection class only deals with Objects, so I would have to cast variables all over the place, and casting in VB6 means actually declaring another variable of that type and using Set to attempt the assignment to perform the “cast”. The only “workaround” is to create a entirely new class that uses a Collection internally but exposes the members of that Collection as the more specific type, as well as having Strongly-Typed parameters for Addition of blocks and removal. But the creation of these classes:
- Is a gigantic pain in the ass. They are almost all exactly the same, differing only in the Type used.
- Clouds the namespace. Poing has a Levels.cls class, a Blocks.cls class, a Balls.cls class, and a few others, all for the sole purpose of representing the exact same data structure for different objects.
- Is a gigantic pain in the ass. I already said that, but I just wanted to make sure I was clear about that.
- While you can use For…Each on the collection, it’s only through non-standard coding and setting method properties that it works, and even then you have very little control since the Interface being used for Enumeration, IEnumVariant is not easily implemented in VB6, and requires literally hacking away at your Process Memory to change in-memory VTables, which wreaks havoc on the brittle IDE.
Now, thanks to my Switch-over to C#, I can just declare those variables to be List<Block> or List<Level> or List<cBall> and forget about all this special Collection class nonsense. And Of course for specific Data Structures, I can use various other Generic classes such as Stack<>, LinkedList<>, etc. Generics is a powerful feature that becomes that much more valuable when you’ve had to hoof it yourself in less powerful languages to duplicate it’s functionality.
Dynamic Loading
But, this is supposed to be about dynamic loading- and more specifically, Reflection. Visual Basic 6 sorta-kinda had reflection, in the form of a MS-provided library, tlbinfo32.dll, which essentially allowed you to inspect COM components; since VB6 produced COM components, this worked there. But it was still rather cumbersome; you were no longer looking at VB-native classes or types, but you had to interpret the appropriate parameters from the idl code. additionally, COM was often just plain stubborn. I feel that in many ways .NET tries to resolve the issues of COM, particular with regards to pluggable components. .NET allows you to, directly in your code, load a new assembly, inspect the classes and types within that assembly- see what those classes implement or derive from, and so forth. Java provides equal functionality with it’s reflection libraries, too, and that is how a lot of java applications implement plugins, including eclipse, one of the better java IDE applications.
With C# & .NET, you can load assemblies directly from the .dll or .exe file; then you have the Assembly object, and can inspect the types of t hat Object. BASeBlock, for example, loads all the assemblies (that don’t meet an “ignore” regular expression) from it’s executable’s folder- I’ll probably change this to a unique location, to prevent the game from enumerating the assemblies I included which will never contain what it is looking for). Once it has all the assemblies, it searches for various Types that implement some of it’s types. It always inspects “itself”, so it will find all the Block derivations, iBallBehaviour implementations, iPaddleBehaviour, etc. These get added to their own LoadedTypeManager instances within the MultiTypeManager that does all the work, and this allows the BASeBlock code to easily find all the implementations of it’s various interfaces, for purposes such as showing a pop-up menu and so forth.
Originally, I wanted to add a single implementation of the appropriate interfaces and base classes, that delegated their actions/events/etc to some sort of script interpreter/compiler. After futzing about with IronPython, however, I decided that the object model exposed there and what I needed required a lot of plumbing code to make managable. But then I had another thought.
The CodeDOM
.NET Exposes a namespace, System.Runtime.CompilerServices that contains more namespaces, classes, and interfaces that are used for dealing with the CodeDOM; it also allows you to compile VB.NET and C# (probably F#, but since that isn’t included in .NET 3.5, I don’t support it yet), directly into assemblies. That is when I realized I could use that functionality, paired with my existing code that finds object implementations and derived classes for an Assembly[] array, to make it possible to “script” new derived classes and implementations of those interfaces and Derivation I search for. The result turned out to be quite simple, not more than a page of code, which I added to the class that performed the Assembly enumeration. It loads new assemblies by inspecting a Scripts folder in it’s %appdata% folder, and attempting to compile those scripts into assemblies (.csx files using the C# code provider, .vb files using the VB code provider… I was going to use .vbx but that gave me flashbacks to Visual Basic 2.0 and vbx extension files. The code used to compile adds the basic references (System.Drawing, System.data.dll, etc) as well as itself, using Assembly.GetExecutingAssembly().Location- this includes the BASeBlock assembly, which is necessary since BASeBlock of course contains all the interfaces that need to be implemented, the necessary frameworks for any additional addins, etc. The script I used to test was a copy-paste of the AddBallBlock class, whose name I changed to AddBallBlockEx. Once the code compiles the new assembly and detects everything went well, that Assembly object is added to the list of Assemblies that the game will inspect for the appropriate derivations- of Block, iBallBehaviour,GamePowerUp etcetera.
After some tweaks and debug sessions, everything seemed to work fine. So far the only issue has been that AddBallBlock’s code called “AddScore” which is implemented by the Block base class, but for some reason trying to compile that resulted in the error that the symbol wasn’t defined. Absolutely no idea what is going on there… Once I resolve that issue, I’ll make the error-detection code a bit more robust and easier to get to (for people trying to write their own scripts, or me in the future, it would be nice to have a place to find the errors issued by the compiler; perhaps it can save them in either the same scripts folder (as script.csx.errors or something) or a separate folder. As it is now, though, the test classes I created seem to be found perfectly fine; the blocks show up in the editor and the powerups can be spawned using cheats, so they seem to work well. the AddScore() issue will bother me though, but it seems to be related to the way the object heirarchy is laid out.
The main “unique” feature of BASeBlock is that it uses pure GDI+; this was because OpenGL seemed like overkill, and I needed to learn GDI+ at the time anyway. People don’t give it enough credit, honestly.
Have something to say about this post? Comment!