Skip to main content

Choosing the right abstraction: Command Routing

Command Routing is a concept inspired on a similar technology found in the Windows Presentation Foundation stack. In essence command routing is about segregation of executable code and the call-site. It also forms an excellent abstraction for game-engines to deal with input and gestures.



Traditional methods such as event based input-handling or polling from an queue have a few shortcomings. They clutter an considerable amount of your code-base with state-machines, hard-coded constants enough to make design change a non-trivial problem to recover from. And to make things worse consider multi-platform where some windowing toolkits don't even support half of the keys they should support (Caps Lock and Glut anyone?).

When we look closer to abstractions of the two, we notice that the traditional dispatching has a conflict in it's identity concerning parallel execution. Traditional dispatching is inherently chronological because changes in the state of an input device are chronological. Automatically implies that any parallel execution of code is 'user-mode scheduled' and thus it cannot be part of the abstraction.  Command Routing circumvents this problem. Here we do not consider changes in the state of input devices as actual event but we consider them as triggers for actual dispatched events which are input device agnostic by themselves.
 void __stdcall MyCommand_Forward( RoutedCommandService::BaseEventArg* eventArgs, FirstPersonCameraController* __restrict thisPtr )
{
if( eventArgs->eventType == RoutedCommandService::KeyDown )
{
if( thisPtr->m_Camera->IsPrimary() == true )
{
thisPtr->state[0] = CameraActionStates_Forward;
}
}
else if( eventArgs->eventType == RoutedCommandService::KeyUp )
{
thisPtr->state[0] = CameraActionStates_None;
}
}

//Setup mandatory command registration
RoutedCommandService::RegisterCommand"CAMERASTRAFEFORWARD");
RoutedCommandService::RegisterCommand("CAMERASTRAFEBACKWARD");
RoutedCommandService::RegisterCommand("CAMERASTRAFELEFT");
RoutedCommandService::RegisterCommand("CAMERASTRAFERIGHT");

//Setup key binding
RoutedCommandService::RegisterInputBinding("CAMERASTRAFEFORWARD", aurora::Key::W, RoutedCommandService::Pressed | RoutedCommandService::Released);
RoutedCommandService::RegisterInputBinding("CAMERASTRAFEBACKWARD", aurora::Key::S, RoutedCommandService::Pressed | RoutedCommandService::Released);
RoutedCommandService::RegisterInputBinding("CAMERASTRAFELEFT", aurora::Key::A, RoutedCommandService::Pressed | RoutedCommandService::Released);
RoutedCommandService::RegisterInputBinding("CAMERASTRAFERIGHT", aurora::Key::D, RoutedCommandService::Pressed | RoutedCommandService::Released);

//Setup execution handler
RoutedCommandService::RegisterCommandBinding("CAMERASTRAFEFORWARD", MyCommand_Forward, this );
RoutedCommandService::RegisterCommandBinding("CAMERASTRAFEBACKWARD", MyCommand_Backward, this );
RoutedCommandService::RegisterCommandBinding("CAMERASTRAFELEFT", MyCommand_StrafeLeft, this );
RoutedCommandService::RegisterCommandBinding("CAMERASTRAFERIGHT", MyCommand_StrafeRight, this );

Above is a small sample code used in my personal code base. First we register an command. After that we attach one or multiple input bindings, input bindings are essentially patterns that need to be matched for the command to be triggered. And lastly we register our execution handler.

Interesting note is that Command Routing from an abstraction point of view doesn't limit itself to just input device state changes as source triggers but it permits any trigger as stimulant. Some of the more complicated scenarios I have considered include events based on the hit-count of a key or on a history of pressed keys, joy-stick gestures and possible exotic hardware such as Kinnect.

 

 

 

Comments

Popular posts from this blog

Material & shader management

In the upcoming changes in my editor I implemented the material system inspired on  Frostbite engine of DICE, binaries are download-able on the project page. Also I've implemented an conversion tool and file-format for future mesh formats using Assimp.

A visual approach to programming

It's been a while since I had opportunity to write anything, with the added misfortune of a hardware deficit but seemingly still had some backups to recover old older entries. Over the years I've taken a interest in language theory and in particular visual programming. Inspired by Unreal Kismet, CryEngine Flow and BitSquid Flow; I too set out myself of creating a similar environment. Primarily I just wanted a visual language as I believed they hold a certain productivity value. My initial designs were of a very object-orientated nature. However this approach just never felt right to me. It means you are going to increase post-deserialization time due to v-table fix-ups but it is also takes dexterity to maintain the code hierarchy required. So what I really wanted to do was design a system a) that reduces post-deserialization times to a bare minimum b) was not inheritance heavy c) small enough to be embeddable. On of the interesting methods that I considered was generating m...

Roadtrip to Germany-Switzerland-Austria-Czech pt. 1

Last month I had the luxury to go down for a road trip through various countries in Germany. Despite it being early fall season we actually had a lot of sunshine, and it was uncanny to see the beautiful scenery we passed through. Our favorite place was on the road from Salzburg to Halstadt where we were headed for the famous sky outlook. We came across a lake surrounded by mountains (presumable alps), the nature is unfathomable.