Today at DevHouse I worked on a couple improvements to the platformer: game state serialization, and one-way barriers.
Game state serialization was the first. My original implementation of saved games only stored the player’s position, so the state of things like switches and doors wouldn’t be persisted – this meant it was trivial to get yourself stuck by loading in an inaccessible location.
The approach I ended up taking was to implement a simple dictionary of named key/value pairs using .NET’s XML Serialization, and then bind that dictionary to all the named objects in the game. I used reflection to tag various properties and fields of objects to indicate that they should be serialized, so that the process of saving and loading the game state was fairly automatic.
What that means is that where before the player’s position was represented by this XML in the save file:
<SavedGame>
<PlayerPosition>
<X>128.449982</X>
<Y>272</Y>
</PlayerPosition>
Now it’s represented by this XML:
<SavedGame>
<GameState>
<types>
<type id="2" name="Microsoft.Xna.Framework.Vector2, Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" />
</types>
<Vector2 key="player.Position" typeId="2">
<X>128.449982</X>
<Y>272</Y>
</Vector2>
You’ll probably notice that the new XML is more verbose (some XML has been omitted from both examples for clarity). The primary difference is that where previously, the values in the SavedGame object were pre-defined, and had to be manually loaded and saved after XML (de)serialization was complete. Now, the game uses the key attached to each value along with its typeId to automatically locate the appropriate object and property.
Once the XML parsing is all done, the following code does all the work of updating the properties with the new values:
protected IEnumerator<object> LoadGame () {
var rtc = new RunToCompletion(Storage.LoadFromStorage<SavedGame>("game.sav"));
yield return rtc;
var sg = (SavedGame)rtc.Result;
var bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
foreach (var item in sg.GameState) {
var parts = item.Key.Split(new char[] { '.' }, 2);
var obj = FindNamedObject(parts[0]);
var t = obj.GetType();
var prop = t.GetProperty(parts[1], bindFlags);
if (prop != null) {
prop.SetValue(obj, item.Value, null);
} else {
var field = t.GetField(parts[1], bindFlags);
if (field != null)
field.SetValue(obj, item.Value);
}
}
}
A bit more verbose than I would like, mostly due to the necessity of handling properties and fields separately, but on the other hand, this code currently handles all the necessary work for loading a saved game.
After I had serialization all rigged up, I started working on implementing one-way barriers, that the player can only pass through in one direction.
One-way barriers required some changes to my existing collision detection code – I extended the existing LevelGeometry interface with a new method called ShouldObstruct. When performing collision detection and movement checks the game now calls ShouldObstruct, passing in the player’s current velocity so that the object can determine whether or not the player should be allowed to pass through the object.
Once that was rigged up, it was as simple as defining a new type of geometry called a OneWayRectangle with a configurable PassableAxis property. The player is only allowed to pass through the rectangle when the dot product of the PassableAxis and the player’s velocity is less or equal to 0. This provides the basic constraint of only being able to move through the barrier in a particular direction.
However, one problem came up after I got it working: It was fairly easy to get stuck inside a barrier by jumping while moving through it, because the barrier would reject the downward falling velocity from the jump and prevent you from landing on the ground. The solution ended up being to always allow the player to move through a barrier if he’s already colliding with it, so that once you’ve crossed into a barrier (which is only possible if you’re moving in the appropriate direction), you’re free to move in any direction until you exit the barrier.
One way barriers turn out to be useful for making environments more dynamic by changing the passable axis of a barrier based on other conditions, or by using a barrier to prevent the player from leaving a chamber once entering. They also provide a good foundation for something I plan to implement in the future – the ability to ‘phase’ through platforms by pressing down and jump, like you can do in many modern 2D platformers.



#1 by Syn on December 15, 2008 - 7:23 pm
Quote
I am impressed on how by using only a few platform components you could make so many fun puzzles. I especially like the unrestricted engine you are building, eg: Wall jumping off sides of platforms, going over the door before it closes. The controls also look good, I can’t wait to try it out.
I was wondering what kind of game you are aiming to create with this engine? Something much like the DS Castlevania series(RPGesque), something with more puzzle orientation or something else.
#2 by Kael on December 15, 2008 - 7:31 pm
Quote
I’m aiming to build a game in the same general vein as the DS castlevanias (primarily order of ecclesia), but with a more puzzle-oriented focus, similar to how the newest prince of persia game is structured.
The game world is ideally going to be one large seamless level full of smaller unique environments, and I’m hoping to come up with a set of gimmicks and motifs that apply to each environment, and then construct my puzzles out of those. As a contrived example, some motifs for a clock tower like in the castlevania games would be spinning gears, pistons, swinging pendulums, etc. where an environment like a cave formed by water erosion would have motifs more fitting to its design – adjustable water levels, destructible barriers, etc.
#3 by name on December 18, 2008 - 12:36 pm
Quote
Interesting. I’ve recently picked up XNA in the attempt to make a basic platformer, and I was led to your page on a google search for resolving collisions on sloped surfaces.
I’ve been making slow progress, mostly due to my lack of motivation, but since I started, which was about a week and a couple days ago, I’ve just been fiddling around with some tutorial code I found on the internet, and the platformer starter kit that XNA 3.0 provides. Watching that Indiana Jones dude in the starter kit run and wallslide and do wall jumps was fun to a certain extent, but I decided to start from scratch, which has been a bit difficult.
I actually found it kind of amusing, because the two primary platformers I’m looking at are the Megaman series, particularly the Zero series, and the Castlevania games, of which I am currently playing Order of Ecclesia, and I thought it was really awesome when I saw that Soma sprite running around.
Despite my best efforts, I doubt my own projects will go too far, as I don’t think I’ll have so much time once winter quarter comes around, but I just wanted to say good job on this, and I’ll probably come back once in a while and check up what’s happening around here.
Pingback: luminance » Content Pipeline integration and deployment