Tuesday, May 31, 2011

DotNetFish - MVVM Part Deux

I only thought I was done refactoring the Projects to use the MVVM pattern. Turns out that two of the three views in the level editor were not converted yet. The Map Select window will not be converted anytime soon either, but its very simple to begin with and not a big deal if it isn't. It cannot be converted at this time because the GMap control does not support MVVM. The other page that I needed to convert is the Edit Level Window, where all of the map editing will be done. It took quite a bit of doing, but I finally forced it into compliance. I am not happy with the results though, and I think some more refactoring is in order.

The biggest challenge that I ran into was getting the my ViewModel for the page, and the custom Map Control to play nicely together. The custom canvas control that I am using does not support any sort of bindings, so I was forced to write my own.

Here is what a dependency property looks like

public static readonly DependencyProperty CurrentPointCommandProperty =
        DependencyProperty.Register(
            "CurrentPointObject",
            typeof(Point),
            typeof(MapCanvas),
            new PropertyMetadata(new PropertyChangedCallback(OnPropertyChanges))
            ); 

Dependency properties are quite easy once you get the hang of it. The string in the example above, is the name of a public property. The first type is the type of the property being referenced, and the second type is the type of whatever object the property resides in. This also contains a Callback when the property changes


private static void OnPropertyChanges(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
 (obj as MapCanvas).InvalidateVisual();
} 

So anytime a property changes the Visual on the mapCanvas is overwritten.

Nothing much to it so far. My biggest concern is the number of  DP's that I have to register just to do something simple.


Now, the second change I made here, was to use the MVVM Light Toolkit instead of the custom implementation I created to handle the commands. This toolkit allowed me in conjuction with the Blend SDK to wire up commands to events that fire on the page rather than the control.

For example

xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
 
<i:Interaction.Triggers>
        <i:EventTrigger EventName="KeyDown">
            <cmd:EventToCommand Command="{Binding OnKeyDown}" PassEventArgsToCommand="True" />
        </i:EventTrigger
</i:Interaction.Triggers>    

This code allows me to bind to the KeyDown Event. It allows my viewmodel to recieve the eventArgs as well. Very useful.

My next step is probably going to involve some refactoring. I think that I can use the MVVM Light messages to help pass information from ViewModel to ViewModel, and also ViewModel to View. Doing so should cut down on the amount of code and Dependency Properties required. Even if most of them are refactored out, I learned a ton about using them and debugging them!



Friday, May 20, 2011

DotNetFish - Sweeping Changes

Got a healthy dose of optimization finished in the map builder. It now approaches each tile from a much lower zoom level, and only goes to a higher zoom level when the map tile has a water/land edge. I'm still not 100% sure that this is the best approach to take., and it may be faster and simpler to process the tiles at a higher zoom level. This is only possible now due to the other changes I made to the build process. For a body of water that has a lot of water and long compared to edges should see a pretty significant improvement, although it will be slower when testing using only edge tiles.

Tiles are now processed much simpler than they were before. Its always difficult to make something simple. I realized that my design was much too complicated when it didn't need to be, so I simplified it significantly. I am now getting much better results, and far fewer error tiles.


I am almost ready to move away from the map generation process, and start focusing on some additions to the editor to allow you to fix error tiles, add spawn locations, etc. I have a few bugs to look at in the build process, and then I plan to focus on the editor.

Monday, May 2, 2011

DotNetFish - Headed towards optimization land

Looks like its time to take a trip towards optimization land.

I knocked one of the items off of my list, which was adding some directionality into the path finding to help it out. Then I attempted to process a small pond. This didn't go so well, as it ground to a halt. The problem I was having is that during debug I was saving each image I was processing. 800 tiles produces 200,000 images in a single directory which was a major source of the slowdown.

After fixing this issue, I will still need to increase the performance. I don't want to spend 4-5 hours building the map of a small lake. So far, I have followed the advanced rule of optimization which states

Don't Optimize Yet

You see, I have not done much performance tuning up to this point, although I did optimize the actual process of processing the tile. I think its important to know when to optimize. I'm now to the point where I need to start processing larger maps, so it makes sense to address this issue now.  I could have made these changes earlier of course, since I understood where the bottleneck was occurring, but there were more important things to work on.

I may have touched on the optimizations that I need to perform, but what it comes down to is processing the edge tiles at the largest magnification, and processing everything else at a much smaller magnification.

Currently, I am processing each 256 tile at the largest zoom setting into  256 tiles. Each of those tiles is having its borders processed as well. A good first optimization would be to only break a tile down into smaller tiles if it actually has an edge on it. Add in zooming out a few levels, and the performance should increase several orders of magnitude.

Currently it took about 6-7 minutes to process 1200 tiles which was about 1000x1000 feet. Given that even a fairly small lake hear my house  is 52,800x 26,400, you can easily understand why this is so important.


On a final note:
After the second run, I had approximately a 4% failure rate. Now I have something to shoot towards beating.

Sunday, May 1, 2011

DotNetFish - A* PathFinding First Pass

It took me a full day to implement, but the map builder can now find a path to an unconnected edge.

The map builder is coming along nicely. I have a few more issues to iron out with the path finding in general that need to be fixed. Once that is done the generation part will be finished for now. Maybe next week I'll be able to hammer out the rest of the path finding.

Current Items of interest
  1. Double edge connections. Loops need to be implemented. For example, is tile A has 2 connections on the same side but nowhere to go, then the tile next to it needs to have a loop added to it,this will require some new tiles.
  2. Need directionality for finding potential matches when looking for target tiles to pathfind to. It should travel away from the starting side first. Only if no potential matches are found should it look behind it.