SilverTile: Working on the game

22. februar 2010

It's time for another project update.

I just uploaded a new version of the SilverTile sample application. The new version has four areas that you can move between freely and lots of monsters you can hunt. The monsters have different AI, the blue ones will try to hunt you while the red ones will move randomly.

Lots of dangerous monsters!

Since my last update I have added some features to the game engine:

  • A simple keyboard input handler
  • Improved area transitions
  • Improved collision detection
  • Layering of sprites (z-index)
  • Improvements to the monster spawning process

All the changes I have done are based on the needs of my sample game. A more structured keyboard input was needed as the complexity of the game grew.

Area transitions needed to be directional so that you can transition into a tile with an area transition back without being stuck in an endless loop.

I found some bugs in the collision detection algorithm that I fixed (Yay for TDD!), but my implementation is really naive. I would be grateful if anyone could help with a better algorithm. :-)

Layering of sprites was something I really wanted the engine to have, but I didn't need it until I was to render the players sword. The sword is drawn underneath the player sprite when the attack animation starts, and I needed to make sure the sword wasn't draw on top of the player.

The monster spawning process was modified so that monsters can be spawned immediately when a level is loaded. The classic The Legend of Zelda spawns enemies this way, so I need to support it.

All game levels were drawn using the editor application I wrote. The application is really simple, but it allows me to draw the game levels and export them as Xaml easily, so I am happy with it.

Whats next

With a more complete game engine I think it is time to build a better user interface for the game. I have added hit points to the player, but I need to display them as hearts as in the classic Zelda.

I also need to build more game levels and start adding items to the game. Monsters should drop coins that you can use to buy stuff.

And then I need to add text rendering and an intro screen and a game over screen!

... So there is still lots of work to do.

kick it on DotNetKicks.com

.NET, Silverlight , ,

Update: The DataTable meets dynamic - improved!

12. februar 2010

A few days ago I blogged about a tiny fun project of mine, where I added dynamic type support to the DataTable and DataRow classes.

This was fun, and the code works as intended, but it has a two shortcomings: There was a bug preventing you from setting values of regular properties on the DynamicDataRow type, and you couldn't add new columns using dynamic properties.

Naturally, I had to fix this, so here it is: The DynamicDataTable v2!

What is new?

Most of the code is the same as last time, but this time the useage is even simpler. You just create a new DynamicDataTable object, add a new row using the NewRow method and assign values to the properties you need. Look at this:

// Create a DataTable
var tb = new DynamicDataTable();

// Row using dynamic syntax
dynamic row = tb.NewRow();
row.aa = "Dynamic here!";
row.bb = 44;
row.cc = DateTime.Now; 
tb.Rows.Add(row);

// Access column values using property accessors instead of using the indexer
Console.WriteLine("AA: " + row.aa);
Console.WriteLine("BB: " + row.bb);
Console.WriteLine("CC: " + row.cc);

If you compare this with the previous version you see that I have removed the column definitions. Now when the DynamicDataRow intercepts a call to a property set for a property that doesn't exist it will check its DataTable to see if there is a column with that name. If it can't find a column with that name it will add a new column and set its datatype to the type of the assigned value. In the above example our table ends up with three columns: "aa" of type String, "bb" of type Int32 and "cc" of type DateTime. Later you can access the DataTable as a regular table if you want.

The updated source code

I have uploaded an updated version of the source code if you are interested: DynamicDataTable.zip (4.57 kb)

kick it on DotNetKicks.com

.NET

Creating a monster: The DataTable meets dynamic

9. februar 2010

This is just for fun, so don't use this at work! :-)

Some day ago we had a discussion on Twitter about using the DataTable and DataSet types in .Net. We talked about the differences between typed and untyped DataSets and the merits of both. 

Personally I don't like typed DataSets, not only do I see them as unnecessary, but the DataSet Designer in Visual Studio is terribly buggy and unstable. The one place where typed DataSets have an advantage is in code readability - using property names is easier to read than accessing the DataRow using the indexer property.

Having just read about the dynamic type support in C#4 I got the idea that we could use it to get the pretty syntax of the typed DataSets without all their extra annoyances. Not that it would be very useful, but it would be a good chance to get to know dynamic typing better while writing some fun code. After all, this combines the "old" techonology of the DataTables with the new fancy technology of dynamic types. What could be better?

What I would like to do

Since this is just for fun, I don't care about writing a very robust implementation. What I want is a way to do the following:

// Create a DataTable object
var tb = new DynamicDataTable();

// Add some columns using old-style syntax
tb.Columns.Add("aa", typeof(string));
tb.Columns.Add("bb", typeof(Int32));

// Add a row using dynamic syntax
dynamic row = tb.NewRow();
row.aa = "Dynamic here!";
row.bb = 42;
tb.Rows.Add(row)

// Access column values using property accessors instead of using the indexer
Console.WriteLine("AA: " + row.aa);
Console.WriteLine("BB: " + row.bb);

Note that this syntax is somewhere between the regular DataTable syntax and the syntax for typed DataTables.

Implementing it

I implemented the DynamicDataTable using three classes: The DynamicDataTable itself, a new DynamicRowsCollection that replaces the DataRowCollection of regular DataTables and the DynamicDataRow that extends the DataRow type with dynamic capabilities.

The DynamicDataTable simply wraps a DataTable and overrides or overwrites a few methods. By overriding the NewRowFromBuilder, GetRowType methods and then overwriting the NewRow method I can force the DynamicDataTable to return my DynamicDataRow objects. In addition I overwrite the Rows collection with my own DynamicRowsCollection type to ensure that we only store DynamicDataRow objects.

public class DynamicDataTable : DataTable
{

  private DynamicRowsCollection _rows;

  protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
  {
    return new DynamicDataRow(builder);
  }

  protected override Type GetRowType()
  {
    return typeof(DynamicDataRow);
  }

  public new DynamicRowsCollection Rows
  { 
    get
    {
      if (_rows == null)
      {
        _rows = new DynamicRowsCollection(base.Rows);
      }
      return _rows;
    }
  }

  public new DynamicDataRow NewRow()
  {
    return (DynamicDataRow)base.NewRow();
  }

}

I then wrote a DynamicRowsCollection type as a replacement for the old RowCollection. It implements the IList<DynamicDataRow> interface but all it does is to reroute all method calls to the contained RowCollection:

public class DynamicRowsCollection : IList<DynamicDataRow>
{

  private DataRowCollection _rows;

  public DynamicRowsCollection(DataRowCollection rows
  {
    _rows = rows;
  }

  #region IList implementation

  // Snipped lots of dumb code...

Finally I implemented the DynamicDataRow. This is my specialized DataRow that reroutes all attemps at getting or setting properties to access the indexer instead. I hit upon a challenge here where I first rerouted all property calls, also those that were to existing properties. This meant that trying to access the DataRow.Table property ended up in an attemt to access a column named Table. To fix this I added a check of whether there already exists a property with the given name. I should also add the same check when setting property values, but I haven't done so yet.

public class DynamicDataRow : System.Data.DataRow, IDynamicMetaObjectProvider
{

  public DynamicDataRow(DataRowBuilder builder) : base(builder)
  { }

  public DynamicMetaObject GetMetaObject(Linq.Expressions.Expression parameter)
  {
    return new DataRowDynamicMetaObject(parameter, this);
  }

  private class DataRowDynamicMetaObject : DynamicMetaObject
  {

    private DataRow _row;

    private DataTable _table;

    public DataRowDynamicMetaObject(Expression parameter, DataRow row)
      : base(parameter, BindingRestrictions.Empty, row)
    {
      _row = row;
      _table = row.Table;
    }

    public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
    {

      // Look for an existing property with this name
      var existingProperty = typeof(DataRow).GetProperty(binder.Name);

      if (existingProperty != null)
      {
        // Call the property directly
        var callProperty = Expression.Property(Expression.Constant(_row),  existingProperty);
        var callPropertyConverted = Expression.Convert(callProperty, binder.ReturnType);
        return new DynamicMetaObject(callPropertyConverted, BindingRestrictions.GetInstanceRestriction(Expression, _row), _row);
      }
      else
      {
        // Try to find a column with the name of the property
        var indexer = typeof(DataRow).GetProperty("Item", new Type[] { typeof(string) });
        var getRow = Expression.Constant(_row);
        var colName = Expression.Constant(binder.Name);
        var getIndexerValue = Expression.Property(getRow, indexer, colName);
        return new DynamicMetaObject(getIndexerValue, BindingRestrictions.GetInstanceRestriction(Expression, _row), _row);
      }

      public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value)
      {

        var indexer = typeof(DataRow).GetProperty("Item", new Type[] { typeof(string) });
        var getRow = Expression.Constant(_row);
        var colName = Expression.Constant(binder.Name);
        var setIndexerValue = Expression.Property(getRow, indexer, colName);
        var newValue = Expression.Constant(value.Value, typeof(object));
        var assignColValue = Expression.Assign(setIndexerValue, newValue);
        return new DynamicMetaObject(assignColValue, BindingRestrictions.GetInstanceRestriction(Expression, _row), _row);
      }
    }
  }

The first thing to note here is that the DynamicDataRow itself is really simple. All it does is to inherit from the DataRow and add an implementation of the IDynamicMetaObjectProvider interface. This interface has only one method named GetMetaObject. My implementation of GetMetaObject is also simple - all it does is to return an instance of a DataRowDynamicMetaObject.

The DataRowDynamicMetaObject is more complex. It implements the BindGetMember and BindSetMember methods that handles all property calls for the dynamic object.

The implementation of BindGetMember will first check if there exists a property with the requested name. This is done by checking the return value of the Type.GetProperty method. If a property exists we just call itand return the value. If the property doesn't exist, we first get a reference to the indexer property of the DataRow. We then call the indexer passing it the name of the property/column we ask for and return the value.

The implementation of BindSetMember is somewhat easier since we don't care about existing properties here. The code is similar to that for BindGetMember but we end up with an Assign expression where we assign the new value to the column.

Possible improvements

My code is not at all meant to be used in a real-life scenario, so I haven't cared about performance of error checking at all. The most obvious improvement would be to cache the generated DynamicMetaObject objects for each property. This could be done using a simple Dictionary shared between all instances of the DataRowDynamicMetaObject type. In addition we should fix the BindSetMember method to check if the property exists on the DataRow type. On top of that we should add some error checking code to make sure the types we assign to the columns are compatible with the columns data type.

Another nice improvement would be to add new columns to the DataTable the first time we assigned a value to a property. This would save us the calls to DataTable.Columns.Add that defines the table.

Conclusion

The dynamic type support in .Net 4 is really nice and opens up some really nice possibilities. It also enables some terrible hacks like the DynamicDataTable.

Feel free to download the source code if you want: DynamicDataTable.zip (4.46 kb)

 

kick it on DotNetKicks.com

.NET