3/23/08

Composition

Well I've been bitten by the Composition bug. In a big way. So to satiate my newfound curiosity, I must confess I've been spending time on an experimental clone of my 2D engine using a technique of dynamic Composition.

The Composition version of the engine is based around a single "Game Object" class. This class is effectively nothing more than a collection of IAugmentation interfaces.

In my existing engine, the Sprite class has become quite large; what's more, not all Sprites always utilize every member and feature of the class. By partitioning this class into several different stand-alone "mini-classes" (all of which implement the IAugmentation interface), these mini-classes become "Augmentations". From this, one can start with an empty game object and build it into anything by adding the appropriate behavioral and/or data Augmentations to it's IAugmentation collection.

What's seductive about the Composition model is that this building process is not significantly different between design time and run time. To me this is the primary advantage of using Composition over Inheritance, which AFAIK is largely static at run time. I.e., once you define your class that inherits from <whatever>, you're stuck with that at run time. With Composition, you could simply remove one Augment and add another.

My only concern with what I've developed so far is that dictionary lookups are becoming somewhat frequent - i.e., I find myself querying whether or not a game object has been augmented with <augmentX>, and if so, retrieving that augment for use. These queries don't exist so much in the Inheritance model, and this aspect may require some fine-tuning as things get progressively more complex. But, we shall see.

Fun stuff!

3/11/08

PointAt v2.0

In Tutorial 6 (The PointAt method), I suggested a way one sprite can "point" at another, given totally arbitrary tree nesting / matrix transform between the two objects. The code was:
 ''' <summary>
 ''' Point one sprite's origin at another sprite's origin
 ''' </summary>
 Public Sub PointAt(ByVal thePointer As Sprite, ByVal theTarget As Sprite)

 Dim objTargetWorldPosition As Vector2

 If theTarget.Parent IsNot Nothing Then
 objTargetWorldPosition = Vector2.Transform(theTarget.Location, theTarget.Parent.DrawMatrix)
 Else
 objTargetWorldPosition = theTarget.Location
 End If

 thePointer.Rotation = 0

 With Vector2.Transform(objTargetWorldPosition, Matrix.Invert(thePointer.DrawMatrix)) - thePointer.Origin

 thePointer.Rotation = CSng(Math.Atan2(.Y, .X) + (Math.PI / 2))

 End With

 End Sub

And that works, but I realized an alternative way to solve the same problem today. I haven't compared the raw performance of this new way vs. the other, so I can't say if it's faster... but I suspect it might be.

 ''' <summary>
 ''' Gets the radian between two sprite origins
 ''' </summary>
 Public Function PointAt(ByVal thePointer As Sprite, ByVal theTarget As Sprite) As Single

 Dim a, b, c As Vector2

 Vector2.Transform(thePointer.Origin, thePointer.DrawMatrix, a)
 Vector2.Transform(theTarget.Origin, theTarget.DrawMatrix, b)

 c = b - a

 Return CSng(Math.Atan2(c.Y, c.X) + MathHelper.PiOver2)

 End Function

I made it a function that returns the radian value, so that you can do with it whatever you so choose - either assign it to the Rotation value of thePointer, or just query the angle between two things for whatever reason.

3/7/08

Uninventing some wheels - Stacks & Dictionaries

You know the "Hashtable(Of Type)" class used by the VBContentManager?

There's actually a generic Dictionary class in the .NET framework that serves the same purpose.

The Dictionary class provides two type predicates, one for the key and one for the value, whereas my Hashtable class simply presumes the key to be of type String. We could do away with the Hashtable(Of Type) class all together, and simply start using Dictionaries.

The other thing I started messing with is the generic Stack class. My current ScreenManager class keeps track of Screen order via some rather sloppy and unnecessary list manipulation. But all of that can be done away with and replaced with a Stack(Of Screen).

Stacks are a very convenient way to maintain Screen order within a ScreenManager, because there is a natural synchronization between the inherent nature of Stacks and the way we display Screens.

At the beginning of the program, push Screen1 onto the Stack. When you transition away from that Screen1 to Screen47, pop the Stack (removing Screen1) and push Screen47 onto the Stack. If you want to display Screen2 over Screen47, simply push Screen2 onto the Stack. Now Screen2 is above Screen47, which is exactly what we wanted to display. When Screen2 is finished, pop it off the Stack, and Screen47 resumes the top position. It's a very convenient way to maintain the order of Screens, and I will employ this technique in the Screen tutorial.

3/5/08

Obligatory update

I've been busier than usual for a while, doing some work on my car (painting wheels - my garage looks, and smells, like a paint shop) and also I'm going through a crunch phase at work, making a great big middle-tier ADO.NET engine which will really expedite creation of business objects & UIs, which is rather cool.

But anyway, I'll be up to my eyeballs in that for another week or so, after that I can get back to continuing with more XNA stuff.