Documentation/Programming Tutorials/Adding a new custom input device

From NeoAxis 3D Engine Wiki

Jump to: navigation, search
Go to higher level
Adding a new custom input device

Contents

Introduction

Following input devices are supported by the engine:

  • mouse,
  • keyboard,
  • joystick, gamepad, racing wheel with Force Feedback support,
  • Xbox 360 controller.

There is an interface for adding support of new input devices. This tutorial tells how to add new input device type to the engine, like Wii Remote or the controller for van simulator.

The example of custom input device implementation is included to SDK (ExampleCustomInputDevice.cs), its source is placed in GameCommon project. In order to execute this example, ExampleCustomInputDevice.InitDevice() line should be uncommented in GameEngineApp.cs file of  Game project.

In order to add new custom device to the engine, it must have a class with implementation in the engine and be registered using input device manager.

The class with the implementation of the device should be inherited from InputDevice class and redeclare two its abstract methods:

JoystickInputDevice class

In order to implement new custom input device class you should use JoystickInputDevice as parent class, it is inherited from InputDevice. This class contains implementation of such device attributes as buttons, axes, navigation buttons, sliders.

The arrays with listed attributes are stored in JoystickInputDevice. The description of these properties is given below:

Name Description
PublicProperty.gif Button[] Buttons Buttons. It is an array with Button elements.
PublicProperty.gif Axis[] Axes Axes. It is an array with Axis elements.
PublicProperty.gif POV[] POVs Navigation buttons. It is an array with POV elements.
PublicProperty.gif Slider[] Sliders Sliders. It is an array with Slider elements.

The classes that implement the elements of controller are described below.

Button

Button class implements the button of controller and it has following properties:

Name Description
PublicProperty.gif JoystickButtons Name The name of the button. It could be chosen from JoystickButtons enumeration.
PublicProperty.gif int Index Integer index of the button. It is used for instant identification of the button.
PublicProperty.gif bool Pressed This property detects is the button pressed or not. The value of the property becomes True when the button is pressed.

Axis

Axis class implements the axis of the controller. The axis could contain one or two values. Axes with two values could be called as "paired axes", which determine two-dimensional direction vector (one axis sets horizontal coordinate and the second one sets vertical coordinate). Steering wheel is a good example of the axis with one value. The position of such axis is set by the rotation of the wheel. The axis has following properties:

Name Description
PublicProperty.gif JoystickAxes Name The name of the axis. It could be chosen from JoystickAxes enumeration.
PublicProperty.gif Range Range Range of the values with floating point. It is set by minimum and maximum values.
PublicProperty.gif bool IsForceFeedbackSupported This property detects the support of force feedback for this axis. If the value of the property is set to True then force feedback could applied to this axis.
PublicProperty.gif float Value Current value which describes current position of the axis.

POV

POV is the class that implements navigation buttons (POVs). POV (Point of View) could have several statuses:

  • center (no navigation buttons are pressed),
  • 4 general directions (North, South, West, East),
  • 4 diagonal directions.
CustomDevice01.jpg

5 general POV statuses are described by JoystickPOVDirections flags:

  • Centered,
  • North,
  • South,
  • East,
  • West.

Intermediate values are set by the pairs of flags that were described above. For example, left and upper direction could be set by the pair of North | West flags.

The properties of POVs:

Name Description
PublicProperty.gif JoystickPOVs Name The name of POV. It could be chosen from JoystickPOVs enumeration.
PublicProperty.gif JoystickPOVDirections Value Current value of this POV. It is a combination of one or several flags JoystickPOVDirections.

Slider

Slider differs from axis by the absence of force feedback support and by having two coordinates. If the slider in real device has only one coordinate then only X coordinate of the slider will be used. Volume control is a good example of the slider.

The properties of the slider:

Name Description
PublicProperty.gif JoystickSliders Name The name of the slider. It could be chosen from JoystickSliders enumeration.
PublicProperty.gif Vec2 Value Current value of the slider. It is set by Vec2 class.

Tutorial - Adding a new custom input device

There is an example of a simple device in SDK. It is placed in ExampleCustomInputDevice.cs file of GameCommon project. In order to activate an example, ExampleCustomInputDevice.InitDevice() line should be uncommented in GameEngineApp.cs file of Game project.

New input device will be added to the engine during this tutorial.

GameCommon module should be added to the project first. In our case it will be implemented in ExampleCustomInputDevice.cs file.

JoystickInputDevice class will be used as a base class, because it's the base class for all game controllers.

public class ExampleCustomInputDevice : JoystickInputDevice
{
}

The constructor of our class should be implemented first. It only calls base constructor of JoystickInputDevice class.

public ExampleCustomInputDevice( string name )
	: base( name )
{
}

The next step is the initialization of the device. The call for special native library, that implements the interface of the device, should be implemented in case of real device. Two buttons and one axis will be created for this tutorial, just as an example.

internal bool Init()
{
	//buttons
	Button[] buttons = new Button[ 2 ];
	buttons[ 0 ] = new Button( JoystickButtons.Button1, 0 );
	buttons[ 1 ] = new Button( JoystickButtons.Button2, 1 );
 
	//axes
	Axis[] axes = new Axis[ 1 ];
	axes[ 0 ] = new JoystickInputDevice.Axis( JoystickAxes.X, new Range( -1, 1 ), false );
 
	//POVs
	POV[] povs = new POV[ 0 ];
 
	//sliders
	Slider[] sliders = new Slider[ 0 ];
 
	//forceFeedbackController
	ForceFeedbackController forceFeedbackController = null;
 
	//initialize data
	InitDeviceData( buttons, axes, povs, sliders, forceFeedbackController );
 
	return true;
}

1) Two buttons are created in this method.

Button[] buttons = new Button[ 2 ];
buttons[ 0 ] = new Button( JoystickButtons.Button1, 0 );
buttons[ 1 ] = new Button( JoystickButtons.Button2, 1 );

2) The axis with the range of values from -1 to 1 is created here, too.

Axis[] axes = new Axis[ 1 ];
axes[ 0 ] = new JoystickInputDevice.Axis( JoystickAxes.X, new Range( -1, 1 ), false );

3) The absence of POVs and sliders is manually set here, too.

//POVs
POV[] povs = new POV[ 0 ];
 
//sliders
Slider[] sliders = new Slider[ 0 ];

4) Our device doesn't support force feedback, let's set this manually here:

ForceFeedbackController forceFeedbackController = null;

The initialization is finished by calling base InitDeviceData method of the base class, that is used for applying all parameters to the device.

The deinitialization should be implemented next to initialization of the device. OnShutdown method should be overridden and should have an empty implementation in our case.

protected override void OnShutdown()
{
}

The next step is overriding of OnUpdateState method. This method is called during every engine tick and it is used for updating the state of the device. The generation of the events by the device is implemented in this method: key press and key up events of the controller and the change of axis position, the change of the direction, the change of slider position.

protected override void OnUpdateState()
{
	//button1
	{
		bool pressed = EngineApp.Instance.IsKeyPressed( EKeys.H );
		if( Buttons[ 0 ].Pressed != pressed )
		{
			if( pressed )
			{
				InputDeviceManager.Instance.SendEvent(
					new JoystickButtonDownEvent( this, Buttons[ 0 ] ) );
			}
			else
			{
				InputDeviceManager.Instance.SendEvent(
					new JoystickButtonUpEvent( this, Buttons[ 0 ] ) );
			}
			Buttons[ 0 ].Pressed = pressed;
		}
	}
 
	//button2
	{
		bool pressed = EngineApp.Instance.IsKeyPressed( EKeys.J );
		if( Buttons[ 1 ].Pressed != pressed )
		{
			if( pressed )
			{
				InputDeviceManager.Instance.SendEvent(
					new JoystickButtonDownEvent( this, Buttons[ 1 ] ) );
			}
			else
			{
				InputDeviceManager.Instance.SendEvent(
					new JoystickButtonUpEvent( this, Buttons[ 1 ] ) );
			}
			Buttons[ 1 ].Pressed = pressed;
		}
	}
 
	//axis X
	{
		float value = MathFunctions.Sin( EngineApp.Instance.Time * 2.0f );
		Axes[ 0 ].Value = value;
		InputDeviceManager.Instance.SendEvent(
			new JoystickAxisChangedEvent( this, Axes[ 0 ] ) );
	}
 
	//custom event example
	{
		//this event will be caused in the EngineApp.OnCustomInputDeviceEvent()
		//and in the all gui controls EControl.OnCustomInputDeviceEvent().
		ExampleCustomInputDeviceSpecialEvent customEvent =
			new ExampleCustomInputDeviceSpecialEvent( this );
		InputDeviceManager.Instance.SendEvent( customEvent );
	}
}

Let's implement the generation of events for our device. In case of real device, we should call its driver and check the status of the button. We should check the status of H button of the keyboard, for example. If the status of the key is changed then such events will be generated: if the key is held then JoystickButtonDownEvent will be generated and if the key is up then JoystickButtonUpEvent will be generated. Current status of the key will be saved to Button.Pressed property after the generation of the event.

bool pressed = EngineApp.Instance.IsKeyPressed( EKeys.H );
if( Buttons[ 0 ].Pressed != pressed )
{
	if( pressed )
	{
		InputDeviceManager.Instance.SendEvent(
			new JoystickButtonDownEvent( this, Buttons[ 0 ] ) );
	}
	else
	{
		InputDeviceManager.Instance.SendEvent(
			new JoystickButtonUpEvent( this, Buttons[ 0 ] ) );
	}
	Buttons[ 0 ].Pressed = pressed;
}

The implementation for the second button will be the same. The event for this button will be generated by pressing J key.

Let's look at the implementation of the event generation for the axis. In case of real device we should call the driver of the device and get the status of the axis. The status of the axis will be calculated using time-dependent function for this example. This value will be saved to the value of X axis - Axis.Value and then the event of the change of axis status will be generated.

float value = MathFunctions.Sin( EngineApp.Instance.Time * 2.0f );
Axes[ 0 ].Value = value;
InputDeviceManager.Instance.SendEvent(new JoystickAxisChangedEvent( this, Axes[ 0 ] ) );

Custom device could generate not only standard events, but custom events, too. Such event could be implemented using special class that should inherit InputEvent. Let's implement this class for special event of input device - ExampleCustomInputDeviceSpecialEvent. It will contain constructor which uses input device as input parameter and calls base constructor.

public class ExampleCustomInputDeviceSpecialEvent : InputEvent
{
	public ExampleCustomInputDeviceSpecialEvent( InputDevice device )
		: base( device )
	{
	}
}

ExampleCustomInputDeviceSpecialEvent event will be generated inside OnUpdateState.

ExampleCustomInputDeviceSpecialEvent customEvent =
	new ExampleCustomInputDeviceSpecialEvent( this );
InputDeviceManager.Instance.SendEvent( customEvent );

The device should registered in input device manager for activation. Let's add static method ExampleCustomInputDevice to our class InitDevice. The instance of our device will be created by this method, Init method will be called and the device will be registered in input device manager.

public static void InitDevice()
{
	if( InputDeviceManager.Instance == null )
		return;
 
	ExampleCustomInputDevice device = new ExampleCustomInputDevice( "ExampleCustomDevice" );
 
	if( !device.Init() )
		return;
 
	InputDeviceManager.Instance.RegisterDevice( device );
}

The created method should called by OnCreate method of GameEngineApp class of Game project.

ExampleCustomInputDevice.InitDevice();

In order to test our device, let's compile all assemblies and run Game.exe. After that we should find Joysticks in Profiler menu and choose our ExampleCustomDevice device from device list (Device). You could read more about testing of game controllers in input system overview.

X value is shown in the menu. If you press H and J keys then buttons Button 1 and Button 2 will be displayed as pressed, too.

CustomDevice02.jpg

You have learned how to implement classes for custom input devices using the class of game controller and generate standard and special events for devices.

Full version of ExampleCustomInputDevice.cs file could be found in GameCommon project of Neoaxis SDK.