GenesisEngine: Input

(I originally posted this on my MSDN blog.)

Once I had the event aggregator set up in GenesisEngine I could think about how to turn keyboard and mouse input into events that other parts of the app could respond to.

The XNA framework doesn’t offer as much help in this area as you might be used to in Windows Forms or WPF.  There isn’t any built-in windowing, or UI controls, or a commanding system so you pretty much have to build your own from scratch.  This isn’t a terribly difficult task, I suppose, but I like the way mine turned out.

XnaInputState

All XNA offers for input is a KeyboardState and MouseState struct every update cycle that contain the current state of the keyboard (the current up or down state of every key) and the current state of the mouse cursor (where it currently is and whether each button is currently up or down).

In order to figure out interesting things like did was a certain key just now pressed this update or has it been held down for awhile, or how far did the mouse move between the last update and this one, you’ve got to track both the last state and the current one and check the differences yourself.  The XnaInputState class handles this responsibility but it’s pretty trivial so I won’t list it here.

InputMapper

The InputMapper class is a bit more interesting.  It stores mappings between states and event messages that should be sent when those states occur, where states in this case mean a key was pressed, or a key is being held down, or the mouse cursor moved.  The mappings are set up in code right now but could be loaded from a config file in the future.  This is from the main Genesis program class:

void SetInputBindings()
{
    _inputMapper.AddKeyDownMessage(Keys.W);
    _inputMapper.AddKeyDownMessage(Keys.S);
    _inputMapper.AddKeyDownMessage(Keys.A);
    _inputMapper.AddKeyDownMessage(Keys.D);
    _inputMapper.AddKeyDownMessage(Keys.E);
    _inputMapper.AddKeyDownMessage(Keys.C);
    _inputMapper.AddKeyDownMessage(Keys.Z);
    _inputMapper.AddKeyPressMessage(Keys.F);
    _inputMapper.AddKeyPressMessage(Keys.U);
    _inputMapper.AddKeyPressMessage(Keys.P);
    _inputMapper.AddKeyPressMessage(Keys.OemPlus);
    _inputMapper.AddKeyPressMessage(Keys.OemMinus);
    _inputMapper.AddKeyPressMessage(Keys.G);
    _inputMapper.AddKeyPressMessage(Keys.Escape);
    // TODO: we don't specify which mouse button must be down
    // (hardcoded to right button ATM),
    // this can be extended when we need to.
    _inputMapper.AddMouseMoveMessage();
}

When a message and an input state are mapped together, InputMapper stores them in lists for later use.  Specifically, it stores a set of delegates that will be executed when the correct input conditions are detected and these delegates send the proper messages to the event aggregator to be forwarded to whomever is interested in them.

I’m creating and storing a delegate that sends an event message rather than simply storing the type of the message because that was the only way I could figure out how to call EventAggregator.SendMessage with a strongly-typed message object.  Essentially I have to capture a generic type parameter, save it away, and later pass it to another generic method without losing its type information.  Creating a delegate at save time accomplishes that.  I’m not thrilled with how obtuse it makes the code but it’s livable for now.  I wouldn’t mind finding a better solution, though.

public void AddKeyPressMessage(Keys key) where T : InputMessage, new()
{
    _keyPressEvents.Add(new KeyEvent { Key = key, Send = x =>
        _eventAggregator.SendMessage(new T { InputState = x}) });
}
public void AddKeyDownMessage(Keys key) where T : InputMessage, new()
{
    _keyDownEvents.Add(new KeyEvent { Key = key, Send = x =>
        _eventAggregator.SendMessage(new T { InputState = x }) });
}
public void AddMouseMoveMessage() where T : InputMessage, new()
{
    _mouseMoveEvents.Add(new MouseMoveEvent { Send = x =>
        _eventAggregator.SendMessage(new T { InputState = x }) });
}
private class KeyEvent
{
    public Keys Key;
    public Action Send;
}
private class MouseMoveEvent
{
    public Action Send;
}

During each update, InputMapper is told to handle input and is passed an IInputState reference.  Based on this input state, it finds any message-sending delegates who’s conditions match the current conditions and executes those delegates.  InputMapper doesn’t know anything about who’s interested in input events, it just fires them.

public void HandleInput(IInputState inputState)
{
    SendKeyPressMessages(inputState);
    SendKeyDownMessages(inputState);
    SendMouseMoveMessages(inputState);
}

private void SendKeyPressMessages(IInputState inputState)
{
    foreach (var keyEvent in _keyPressEvents.Where(keyEvent =>
        inputState.IsKeyPressed(keyEvent.Key)))
    {
        keyEvent.Send(inputState);
    }
}

I like how the responsibilities are clearly separated in this system:

  1. Tracking input state changes – XnaInputState
  2. Firing events based on the current input state – InputMapper
  3. Actually sending the events to listeners – EventAggregator
  4. Receiving and acting on events – Implementers of IListener

I think it would be interesting to see how to incorporate the new Reactive Extensions into the input system.  Rather than checking the current input state against a set of mappings every time, the InputMapper would set up some LINQ expressions against an input event sequence.  I haven’t tried using the Reactive Extensions yet but from what I’ve read so far it seems like it should simplify the concepts here.

Advertisements

GenesisEngine: The Event Aggregator

(I originally posted this on my MSDN blog.)

GenesisEngine is still a pretty small code base at this point but there are some design elements that I’m pretty happy with.  I’ll run through a series of posts describing these parts so that people can learn from them or maybe critique them and find ways to make them even better.

Event Aggregator

I lifted the design of the event aggregator directly from Jeremy Miller’s posts on the subject and from his implementation in StoryTeller.  There’s not much I can add to what Jeremy’s already said but I’ll summarize the concept.

Here’s the very small interface for my aggregator:

public interface IEventAggregator
{
    void SendMessage(T message);
    void AddListener(object listener);
}

An event, or message, can be any type you want to use.  You can put as much context data as you wish into your message object.  When you send a message, the message object is forwarded to all listeners who have stated a desire to receive that type of message.

When you add listeners to the event aggregator you don’t explicitly list what messages you want the listener to receive.  Instead, the listener’s class definition is marked up by implementing one or more flavors of IListener, like so:

public interface IListener
{
    void Handle(T message);
}

public class Settings : ISettings,
                        INotifyPropertyChanged,
                        IListener,
                        IListener,
                        IListener,
                        IListener,
                        IListener
{
    // Class stuff here
}

The event aggregator looks for those interfaces to figure out which messages the listener is interested in:

public void SendMessage(T message)
{
    IEnumerable recipients;
    lock (_lockObject)
    {
        recipients = FindEligibleListeners();
    }
    SendMessageToRecipients(message, recipients);
}

private IEnumerable FindEligibleListeners()
{
    var eligibleListeners = new List();
    foreach (var weakReference in _listeners)
    {
        // We need to create a strong reference before testing aliveness
        // so that the GC doesn't yank it out from under us.  Don't convert
        // this to a LINQ expression because it doesn't guarentee that behavior
        var strongReference = weakReference.Target as IListener;
        if (strongReference != null)
        {
            eligibleListeners.Add(strongReference);
        }
    }
    return eligibleListeners;
}

I use StructureMap as my IoC container and I’m using Jeremy’s neat trick of auto-registering listeners when they are created by the container, so most of the time I don’t even have to explicitly add listeners to the aggregator:

public class EventAggregatorTypeInterceptor : TypeInterceptor
{
    public object Process(object target, IContext context)
    {
        context.GetInstance().AddListener(target);
        return target;
    }

    public bool MatchesType(Type type)
    {
        return type.ImplementsInterfaceTemplate(typeof(IListener));
    }
}

I don’t have a RemoveListener() method on my event aggregator right now for two reasons:

  1. The event aggregator holds weak references to all listeners so it’s not necessary to explicitly remove them for garbage-collection purposes.
  2. So far I haven’t had a need to remove a listener before the end of its lifetime so there’s been no need to implement that functionality yet.

I’m very happy with this eventing design.  It’s simple, has low overhead, and just feels elegant to me.

The source for GenesisEngine is available here.

GenesisEngine: Don’t Get Domain Objects From The Container

(I originally posted this on my MSDN blog.)

IoC containers are awesome and I use them in all my non-trivial projects.  However, there’s an interesting caveat when it comes to using IoC containers that isn’t totally obvious: don’t get domain entities straight from the container.

Surprisingly, this rule doesn’t seem to have a lot of source material.  It’s well-known among the IoC experts (you might run into terse “don’t do that” comments on mailing lists and discussion boards) but I’ve had a really hard time finding a clear, concise explanation for exactly why getting domain objects from the container is an anti-pattern.  Maybe my web search kung fu is weak, I dunno.  This post is about the best I’ve been able to find and even it doesn’t do into the reasons behind the rule.  If anyone has a better source, let me know.

Unfortunately I’m not going to write that definitive explanation either but I can at least demonstrate what happened when I ignored it.  I already knew that domain objects don’t belong in the container before I started writing GenesisEngine but for some reason I wasn’t really thinking of the Planet class (and all of its component classes) as domain objects.  I’m not sure why; probably because I started with the assumption that there would be one and only one planet, but of course it’s really a domain entity because it has uniquely identifying information (its location and radius).

So what happened?

How it was

My MainPresenter was created from the container and received an injected IPlanet from the container, like so:

public MainPresenter(IPlanet planet, ICamera camera, IWindowManager windowManager,

    Statistics statistics, ISettings settings)

{

    _planet = planet;

    _planet.Initialize(DoubleVector3.Zero, PhysicalConstants.RadiusOfEarth);

    _camera = camera;

    _windowManager = windowManager;

    _statistics = statistics;

    _windowManager.ShowAllWindows();

    _settings = settings;

    _settings.ShouldUpdate = true;

}

The container created the Planet object but it couldn’t give it any uniquely identifying information because the container didn’t know anything about what kind of planet I wanted to create.  So in the MainPresenter constructor, I had to call IPlanet::Initialize() to pass in the unique attributes.  This two-stage construction is a definite anti-pattern because there’s no safety net to prevent the consumer of IPlanet from forgetting to initialize the injected dependency.

Here’s the construction and initialization of the planet class where you can see the two-stage construction.  Note that the concept of “inject then initialize” was cascading to the Planet dependencies like the PlanetRenderer as well.

public Planet(IPlanetRenderer renderer, ITerrainFactory terrainFactory,

    IHeightfieldGenerator generator, Statistics statistics)

{

    _renderer = renderer;

    _terrainFactory = terrainFactory;

    _generator = generator;

    _statistics = statistics;

    _clippingPlanes = new ClippingPlanes();

}

public void Initialize(DoubleVector3 location, double radius)

{

    _location = location;

    _radius = radius;

    CreateTerrain();

    _renderer.Initialize(_radius);

}

Aside from obtuse code that was likely to break, my main problem was that I could only have one planet in my program.  What if I wanted to put a moon in orbit around my planet, or model a solar system?  Clearly injecting planets from the container into the MainPresenter wasn’t the right approach.  I guess I could have had the MainPresenter grab additional planets straight from the container itself, but it’s a code smell to reference the container outside of bootstrapper/setup code.

Interestingly, this poor design was indirectly causing bizarre problems in other seemingly-unrelated areas of the code.  For instance, I had this comment in my initialize method for the main Genesis program class:

protected override void Initialize()

{

    this.IsMouseVisible = true;

    _mainPresenter = ObjectFactory.GetInstance<MainPresenter>();

    // TODO: we really don’t need a reference to the camera controller here but

    // no one else seems to need a reference to it either and it needs to be

    // created otherwise the event aggregator won’t subscribe it.  Is there a

    // better way to handle this?

    _cameraController = ObjectFactory.GetInstance<ICameraController>();

    _inputState = ObjectFactory.GetInstance<IInputState>();

    _inputMapper = ObjectFactory.GetInstance<IInputMapper>();

    SetInputBindings();

    base.Initialize();

}

I had a CameraController object that needed to exist but nothing in the whole app seemed to have a reference to it.  I had to hack in a reference in my main program class just to force the object to be created and to stay around.  Turns out that problem was caused by my pulling domain objects out of the container.  The IPlanet was being directly injected into the CameraController when it should have gone a different route.

How I fixed it

Fixing this kind of mess usually involves factories.

Rather than injecting the IPlanet from the container I injected an IPlanetFactory into the MainPresenter.  MainPresenter can now use the planet factory to create as many planets as it needs.  It turns out that MainPresenter now has kind of a two-step creation process – construct it then show it, but that makes logical sense for a presenter and actually helps straighten out the flow of showing other windows, etc.

public MainPresenter(IPlanetFactory planetFactory, ICamera camera, ICameraController cameraController, IWindowManager windowManager, Statistics statistics, ISettings settings)

{

    _planetFactory = planetFactory;

    _camera = camera;

    _cameraController = cameraController;

    _windowManager = windowManager;

    _statistics = statistics;

    _settings = settings;

    _settings.ShouldUpdate = true;

}

public void Show()

{

    _planet = _planetFactory.Create(DoubleVector3.Zero, PhysicalConstants.RadiusOfEarth);

    _cameraController.AttachToPlanet(_planet);

    _windowManager.ShowAllWindows();

}

Note that the CameraController is injected into the MainPresenter and is explicitly attached to a particular planet which fixed my phantom CameraController problem since now the container actually needs to create it and MainPresenter holds a reference to it.  The bizzaro reference I had in the Genesis program class went away and the handling of MainPresenter is more logical (get it from the container and then show it).

protected override void Initialize()

{

    this.IsMouseVisible = true;

    _mainPresenter = ObjectFactory.GetInstance<MainPresenter>();

    _mainPresenter.Show();

    _inputState = ObjectFactory.GetInstance<IInputState>();

    _inputMapper = ObjectFactory.GetInstance<IInputMapper>();

    SetInputBindings();

    base.Initialize();

}

Finally, the Planet class is simpler and there’s no cascading Initialize calls to the renderer any more:

public Planet(DoubleVector3 location, double radius, ITerrain terrain,

    IPlanetRenderer renderer, IHeightfieldGenerator generator, Statistics statistics)

{

    _location = location;

    _radius = radius;

    _terrain = terrain;

    _renderer = renderer;

    _generator = generator;

    _statistics = statistics;

    _clippingPlanes = new ClippingPlanes();

}

You can see the GitHub Compare View of all of the changes here.

The GenesisEngine Project

(I originally posted this on my MSDN blog.)

I’ve been working on a personal project off and on for awhile now.  It’s called GenesisEngine and it’s a program that generates and renders a procedurally-generated planet.  I can take in the view down at ground level with the terrain generated at ~1-meter precision, or I can zoom all the way out into high orbit and see the entire planet at once.  This requires relatively little memory and no storage because the whole landscape is generated on demand via fractal algorithms.

Now that I’ve got it to a decent alpha state where it renders mildly interesting terrain I decided to throw it up on GitHub for public amusement.  I don’t have any grand plans for this project; it’s just a little laboratory where I can experiment with new code design ideas or new tools, and in the process maybe build something that looks cool and teaches me more about graphics and other stuff that I’m interested in.  It’s already been a useful experience for me because I’ve taken several concepts that I read about, tried them out in GenesisEngine, and incorporated them into my code bases at work.

Along the way I plan to blog about the lessons I learn.  I’m certainly not blazing any new trails here; I’m just picking up good ideas from people who are much smarter than me and implementing them in a non-trivial project.  I hope my successes and failures, and the source code itself, might be of use to others who want to learn.

The code can be found at at http://github.com/SaintGimp/GenesisEngine.  Git is another thing I’ve been learning about thanks to GenesisEngine.  It’s well worth the time to explore either Git or Mercurial or both if you’re not already familiar with distributed version control systems.  It’s slightly bewildering at first to a Perforce/TFS guy but I’m beginning to see what people are so excited about.

If you want to download and build it you’ll first need to install Visual Studio 2008 (you can use the C# Express Edition if you like) and XNA Game Studio 3.1.  Everything else should be included in the repository.  You should probably check the README file for additional information.

I want to emphasize a couple of points:

  • This is absolutely not a Microsoft sample/demo/guidance project.  This is my own work that I’m doing on my own time.
  • There are areas of the code that I’m pretty happy with and areas that I know are abominable and need to be reworked.  This is not a tutorial or a best-practices showcase.  This is just a real-world developer working on real-world code.  I suspect the best lessons to be drawn from this project will be not how the code looks at any particular point in time but rather how it grows and evolves over time.

Ok, is that enough caveats, self-deprecations, and disclaimers?  Anyway, here are a few screenshots to illustrate what I’ve got so far.  There’s no texturing or atmosphere yet so think “ice moon”.  The red lines mark the boundaries of the individual terrain patches.

On the ground:

image_10.png

At airplane level:

image_2.png

Low orbit:

image_4.png

High orbit:

image_6.png