04 Jun 2012 @ 12:16 AM 

The currently released version of BASeBlock is 2.3.0. I have made a lot of changes to the game, added a few blocks, abilities, and other fun stuff, and refactored various parts of the code to make things work better since then. One of the biggest new features is “framerate independence”. 2.3.0 and earlier versions basically did velocity like this for every game tick:

However, the faster the game loop ran, the more times this would run, and typically the higher the fps the more the game loop would run too. This meant that the speed of objects could be the same internally but visibly the objects seemed to move at wildly different speeds. The “fix” to this is relatively simple- instead of simply adding the velocity to the location, we need to take into account some other factors. First, we analyze the problem. What do we want to achieve? The quick answer is “we want the movement of objects to remain equal regardless of how fast the game ticks go”. The best way I’ve found is to choose a given framerate as the “ideal” framerate; if the game runs at this fps, than the result would be that the velocity is added verbatim; If the framerate is less, than we add “more” to compensate; for example, a framerate of 30 in this case would double all speed additions that are performed; and a framerate of 120 would half them.

BASeBlock already tracks the FPS, so the solution was three-fold; first, create a routine that would retrieve the appropriate multiplier based on the framerate and the desired framerate, next, create a routine to simplify the incrementing of a location with a velocity that would take into account the current multiplier that was derived from the framerate, and also to change all the code that simply adds them to use the new routine.

Implementing this in BASeBlock was something I was wont to do for quite some time; it seemed a lot more involved than it really was. Eventually I just decided to try; if things went sour I could always roll back to a previous SVN commit anyway.is has

First, I added the routine for getting the game Multiplier. This required the current FPS of the game. Since that seems like something best dealt with in the presentation layer (and also since the main game form was already tracking FPS for the FPS counter) I simply added a property to the IClientObject interface, which is designed to allow for a way for the form and the game logic to communicate without explicitly requiring knowledge on what it is communicating with. With that property in place, I simply implemented the multiplier routine as a basic division- the DesiredFPS divided by the current FPS. (There is an exception for the case where the retrieved FPS is 0 where it will return 1 for the multiplier). One very interesting side effect of this is that I could, if I wanted, “fake” slow motion by munging around with the CurrentFPS as returned by the clientObject, though that is probably not a good use of this design.

I then implemented a simple routine for incrementing the location, not surprisingly I called this “IncrementLocation”. It adds the velocity, but multiplies it by the multiplier as derived from the currentFPS and desired FPS.

This worked rather well, once I found and replaced all the old direct-addition code with a call to this routine. However there were still some odd behaviours; mostly related to velocity decay. Some objects- particles, the gamecharacter’s jumping, some items falling, and whatnot would reduce or increase their speed by multiplying components of that speed by a set factor. For example, a particle might “slow down” after it spawned by multiplying it’s X and Y speed by 0.98 each frame. I needed to make similar adjustments to the multiplications factors there in much the same manner as for the additions.

I still encounter minor issues that are a direct result of the changes to a “managed” framerate concept; a nice benefit compared to 2.3.0 is that I was able to remove the silly Thread.Sleep() call that slept for 5 or 50 milliseconds (I forget specifically) so the framerate is typically higher; on the “Spartan” Level set builder, the framerate is usually close to 200, which is pretty good for GDI+, and that’s the debug build, too, which is slower than release.

After this, I tried to improve the platforming elements of the game a bit more. I added some new powers, fixed a few minor issues with some of the powerup management code, and added a new interface for the editor to allow blocks to draw something “special” when being shown in the editor; this is used by the powerup block to show the contents of itself as well as modify the tooltip shown. Another change was “block tracking” at the level of the PlatformObject. This also sounded a lot more complex than it was. The idea was simple- when the character, or anything, is on a block, we want them to move with it. This was done by having the platform object track any block it is on, then, each frame, adding the distance the block moved, if any, to it’s own location as well.This has worked spectacularly. I also added an interface for blocks so they can receive notifications from a platformobject when they are stood on.

There is a bit of a downside to this idea, though, based on how I implemented some other “moving” block features for performance reasons. I have a few blocks that give the illusion of moving when hit, but in fact destroy themselves and spawn another object in their place that looks the same. These blocks include BlockShotBlock, BallDirectShotBlock, and the “magnetAttractor” block; the first one gives the appearance of shooting upwards when hit, breaking all blocks in it’s path; the second goes in the direction the ball that hit it was going, and the third works in tandem with another instance of a magnetAttractor block to create the illusion of the two blocks flying towards each other and exploding, or flying apart. These rely on GameObjects to control their behaviours after they are hit, allowing themselves to be destroyed and allowing the rest of their “action” to be governed by those objects. Most specifically, the “BoxDestructor” which is used to create a block-shaped projectile that can destroy other blocks. The magnetAttractor creates two such blocks when necessary, and controls them with yet another gameobject that handles their velocity change, and detects when they meet, creating the requisite explosion. I did it this way because my animatedBlock “architecture” is terrible and annoying to work with, or, at least it was at the time. This means that a gamecharacter cannot stand on such a block and be “fired” along with it, which would have been an awesome gameplay principle for level design. I did create a movingplatform block that opens up some neat possibilities too, though. And causes some really goofy gameplay when I replace all the blocks in a level with them.

My next endeavour was related to the editor; With the new platforming component, I had made it possible to create a Platform-oriented level, with or without a paddle, by adding the appropriate triggers and components to a level. I forgot to add some of these more than once; in fact the second level of the “testplatforming5.blf” levelset included with 2.6 forgot to set the autorespawn field of one of the spawner blocks, meaning that once you die, you cannot beat the level, since only the paddle respawns, not the character. To help alleviate this, I decided to create “templates”. This means that when adding a new level, as well as being able to just add a blank level, one can create a new level copied from a template. This really added a richness to the editor. Templates are loaded from the templates directory, and can be shown either in a categorized drop-down or in a categorized dialog; the “category” design derives from the template concept used with tools such as Visual Studio itself or VB6, which separates the templates into separate categories. This should make the creation of custom levels, particularly platforming levels, far easier. Templates can also add sounds or images to the loaded Set. (possible revisions might be to warn when a template object conflicts with an existing resource, rather than replacing it).

I also fixed a myriad of other bugs and UI issues that I encountered while working on other features. The newer version is really shaping up to be a great update.

Posted By: BC_Programming
Last Edit: 04 Jun 2012 @ 12:16 AM

EmailPermalinkComments Off on BASeBlock 2.4.0 Dev notes
Tags
 12 Jan 2012 @ 3:11 PM 

In some of my recent posts, I’ve covered the topic of accessing and parsing an INI file for configuration data in a C# Application.

Some may wonder why. After all; the “norm” for C# and .NET applications is to use XML files for configuration information, isn’t it? Well, yes. But to be honest, XML files are a fucking pain in the ass. They aren’t human readable to your average person the same way an INI file is, and getting/setting values is tedious. Primarily, the reason I use INI files is that they are:

  1. Human Readable: Anybody can understand the basic structure of the sections and Name=Value syntax.
  2. Accessible: You don’t need a special editor
  3. Portable: since the entire thing is interpreted using Managed code, it will act the same on any platform (Mono or the MS CLR).

Mostly, I feel that XML, and in many ways other configuration options, are more or less driven by fad. Another option for configuration settings on Windows is the Registry, which is in fact often the recommended method; but this is anything but accessible to the user. Would you rather guide a user to edit a INI file or to fiddle with registry settings?

With that said, INI Files do have their own issues. For example, their data is typically typeless; or, more precisely, the Values are all strings. Whereas using a .NET XML Serializer, for example, you could easily(relatively speaking) serialize and deserialize a special configuration class to and from an XML file and preserve it’s format, with my INI file class there will typically be some work to parse the values.

It was with the idea of turning my string-only INIFile configuration settings into something that can be used for nearly any type that I created the INItemValueExtensions class, which is nothing more than a static class that provides some extension methods for the INIDataItem class. I covered this in my previous post.

The prototypes for the two static functions are:

How would one use these extension methods? Well, here’s an Example:

Woah, hold the phone! What’s going on here? We’re loading DateTime values directly from the INI File? How does that work?

All the “magic” happens in the getValue generic extension method. The first thing the routine does is check to see if the Type Parameter has a static TryParse() method; if it implements ISerializable and have a TryParse method, than the routine will read the string from the INI file, decode it via Base64, and throw it in a MemoryStream, and then try to deserialize the Object Graph for a Type T using that stream.

If it does implement a TryParse() routine, (like, for example, DateTime) it doesn’t try quite as hard. It takes the string from the INI file and hands it to the Type’s TryParse() routine, and then returns what that gives back. Naturally, the inverse function (setValue) does something somewhat opposite; it checks the Base64 logic, and if so it sets the value of the item to the Base64 encoded value of the serialized object. Otherwise, it just uses toString().

This typically works, particularly with DateTime, because usually ToString() is the inverse of TryParse(). In the case of DateTime, this has a few edge cases with regards to locale, but usually it works quite well. And more importantly, the introduction of allowing any object that implements ISerializable to simply be thrown as an INI value via a Base64 encoded string is useful too, although with large objects it’s probably not a good idea for obvious reasons.

But… I still want to access other settings!

Of Course, an INIFile is only one of any number of ways to store/retrieve configuration settings. And while they don’t typically lend themselves to the same syntax provided by the INIFile class, it would be useful to have some sort of common denominator that can handle it all. That was the original intent of the relatively unassuming ISettingsStorage interface:

This uses a concept known as a “category” which is pretty much the same idea as an INI File section. What makes it different is that, for implementors that use other storage mechanisms, it could have additional meaning; for example, a fictitious XML implementation of ISettingsStorage could use the “Category” string as an XPath to an element; and the Value could be stored/retrieved as a Attribute. a Registry implementation might use it as a Registry path, and so on.

The problem is, even though the INIFile class implements this interface, it’s too basic, and doesn’t provide nearly the syntactic cleanliness that just using the INIFile does. Stemming from that, and because I wanted to try to get a way to store settings directly in a DB, I introduced two events to the INIFile class; one that fires when a Value is retrieved, and one when a value is saved. This way, the event could be hooked and the value saved elsewhere, If desired. Now, to be fair, this is mostly a shortcoming of my interface definition; as you can see above, there is no way to, for example, inspect category or Value names. I toyed with the idea of adding a “psuedo” category/value combination that would return a delimited string of category names, but that felt extremely silly. The creation of a generic interface- or abstract class- that provides all the conveniences I currently enjoy using my INIFile class but allowing me to also use XML, Registry, or nearly any other persistent storage for settings will be a long term goal. For now, I’m content with accessing INI files and having a unclean event to hack in my own behaviour.

My first test of the above feature- whereby it allows values to be TryParse’d and ToString’d back and forth from a given type on the fly- was the creation of a FormPositionSaver class.

The proper way to save and restore a window’s position on Windows is using the GetWindowPlacement() and SetWindowPlacement() API Functions. These use a structure, named, quite aptly, “WINDOWPLACEMENT” to retrieve and set the window position and various attributes. Therefore, our first task is to create the proper P/Invoke’s for these functions:

I also include OffsetRect(), but I’ll get to that in a bit. Now the “big one” is the definition of the WINDOWPLACEMENT structure and it’s various aggregate structures. Why? well, in the interest of leveraging the INIFile’s static extensions, Why not define a static TryParse() and a toString() method on the structure that can set and retrieve the member values:

WHEW! that’s quite a bit of code for a structure definition, but we’ll make up for it with the brevity of the actual FormPositionSaver class itself. First, my design goal with this class was to make it basically do all the heavy lifting; it hooks both the Load and Unload event, and saves to and from a given INIFile Object in those events. Since the application I was working on at the time didn’t actually get a Valid INI object until during it’s main form’s Load event, and since there is no way to say “Invoke this event first no matter what” I also added a way for it to be told that hooking the load event would be pointless since it already occured, at which point it will not hook the event and instead set the form position immediately. Values are stored

Alright, so maybe I lied a bit. It’s not super short. Although a lot of it is comments. Some might note that I only sporadically add doc comments, even though I ought to be adding them everywhere. Well, sue me. I just add them when I feel like it. When I’m concentrating on function, I’m not one to give creedence to form.

This is where I explain OffsetRect(). Basically, if your application is run twice, and you load the form position twice, the second form will open over the first one, and the screen will look pretty much the same. So we detect previous instances and offset by an amount to make it’s position different from any previous instances as necessary. That’s pretty much the only purpose of OffsetRect.

I have packaged the current versions of cINIFile.cs and the new FormPositionSaver.cs in a zip file, it can be downloaded from here.

Posted By: BC_Programming
Last Edit: 12 Jan 2012 @ 03:11 PM

EmailPermalinkComments Off on Why I use INI Files, and a FormPositionSaver class
Tags

 Last 50 Posts
 Back
Change Theme...
  • Users » 47469
  • Posts/Pages » 391
  • Comments » 105

PP



    No Child Pages.

Windows optimization tips



    No Child Pages.

Soft. Picks



    No Child Pages.

VS Fixes



    No Child Pages.

PC Build 1: “FASTLORD”



    No Child Pages.