【译】Microsoft Robotics Studio – 一个手柄服务

发表于2016-03-09
评论0 1.3k浏览
   原文地址:http://www.codeproject.com/Articles/20393/Microsoft-Robotics-Studio-A-pad-service
  版权:作者版权声明为http://www.codeproject.com/info/cpol10.aspx,可以翻译

一、简介
  在这篇文章里面你将看到怎么用Microsoft Robotics来创建一个游戏手柄服务。这篇文章不是CCR或者DSS的教程,对于这些技术有了解是非常有用的,这篇文章将假设你知道什么是消息、队列以及服务。
  基本的想法很简单:服务每隔x毫秒调用一个手柄服务。手柄服务调用rect input并且返回-1到1。比如左上角是(-1,1),右下角是(1,-1)。

 

  这个过程中不需要计时器或者线程。我将使用消息、队列和CCR类。

二、背景
  对于硬件(手柄)说几句。我使用的是Logitech Dual Action Gamepad。对于游戏来说是一个很好很便宜的设备,精度足够控制机器人。我没用过Direct Input所以我被迫在代码里面硬编码数字。我假设这些数字是和设备相关的,所以如果你用了一个不同的设备,你要改变这些地方。
  在下个版本里面,我将使用XML做为配置文件。
为了编译,你必须有Microsoft.DirectX.DirectInput.dll文件,你可以在Directx SDK找到它。
工程
  要创建一个空服务,你有两个办法:从一个空白的解决方案开始或者使用DssNewService.exe。我将使用较容易的那种:打开MR的控制行窗口然后cd到samples目录。然后启动DssNewService.exe。这将创建一个包括所有必要数据的新工程。这个工程还有一个后构建行为来产生代理类。
  另外创建的一个文件是Manifest.xml和PadTypes.cs文件。PadTypes包含了服务需要使用的消息。然后定义你服务要响应的行为。
  打开解决方案并向Microsoft.DirectX.DirectInput.dll添加引用。添加:
using Microsoft.DirectX.DirectInput;
  你现在可以使用Direct Input了。
  服务分成三个逻辑部分:
  l  Direct Input初始化

三、l  Direct Input轮询
  l  消息提醒
  Direct Input初始化
//private class variable
private Device _gamepad = null;
...
//Init direct input
DeviceList devlist =Manager.GetDevices(DeviceClass.GameControl,
                    EnumDevicesFlags.AttachedOnly);
foreach (DeviceInstance inst in devlist)
{
    Guid g =inst.InstanceGuid;
    _gamepad = new Device(g);
    _gamepad.SetCooperativeLevel(_form.Handle,
        CooperativeLevelFlags.Background |
        CooperativeLevelFlags.NonExclusive);
}
if (_gamepad != null)
{
    _gamepad.SetDataFormat(DeviceDataFormat.Joystick);
    _gamepad.Acquire();
}
  现在我们可以用_gamepad查询手柄的状态,去看按键值或者按钮状态。_form仅仅是用来初始化DirectInput的,还可以提供对手柄的图形反馈。
  注意!如果我在VS2005里面开始调试,我会得到一个“Loader Lock”的警告。这是Manageddebugging assistants引起的。所以你可以如文章描述那样禁用这个服务,或者运行程序以后再附加上去。

四、Direct Input轮询
  手柄询分成2块。第一个块是基于一个计时器:在每次tick读一次手柄的数据,数据会被立刻返回。会激活一个Arbiter,异步委托进程的消息和事件会发送到主网关。
//the timer port
private Port _timerPort = null;
...
_timerPort = new Port();
...
_timerPort.Post(DateTime.Now);
Activate(Arbiter.Receive(true, _timerPort,
    delegate(DateTime sign)
    {
        Activate(//every 75 ms ...
            Arbiter.Receive(false, TimeoutPort(75),
               delegate(DateTime time)
               {
                   //poll the gamepad device and enqueue the data read
                   if (_gamepad != null)
                   {
                        // ... poll the pad
                       JoystickState jstate = default(JoystickState);
                       _gamepad.Poll();
                       jstate = _gamepad.CurrentJoystickState;
                       _joyReads.Post(jstate);
                   }
                   _timerPort.Post(time);
               }
            )
        );
    }
));
  另外一个arbiter是在_joyReads被激活的。
Activate(
    Arbiter.Receive(true, _joyReads,
        delegate(JoystickState jsta)
        {
            //jsta analysis inorder to send external notifications
        })
);
  这段代码读当前的按键值(L,R)并把它们归一化到(-1,1)。
RightStickUpdate actR = new RightStickUpdate();
actR.Body.Stick.StickValueX =_state.RightStick.StickValueX;
actR.Body.Stick.StickValueY =_state.RightStick.StickValueY;
LogInfo("RightStickUpdate " +actR.Body.Stick.StickValueX,
                        actR.Body.Stick.StickValueY);
_mainPort.Post(actR);
  RightStickUpdate是一个定义在PadType.cs的类
public class RightStickUpdate :
    Update
{
    private StickStatus _stick;
    [DataMember]
    public StickStatusStickStatus
    {
        get { return _stick; }
        set { _stick = value; }
    }
    public RightStickUpdate()
        : base(new StickUpdateResponseRight())
    {
        _stick= base.Body.Stick;
    }
}
  最后一步是在消息处理函数里面,如下:
 [ServiceHandler(ServiceHandlerBehavior.Exclusive)]
public IEnumeratorRightStickUpdateHandler(RightStickUpdate rsUpd)
{
    rsUpd.ResponsePort.Post(DefaultUpdateResponseType.Instance);
    //notify it
    SendNotification(_submgrPort, rsUpd);
    yield break;
}
  提交一个反馈并且发送一个通知。在解决方案内的其他消息都是来处理按键和按钮的。

五、GUI
  很快会开发GUI来返回手柄状态。为了表现确实是内部服务。PadInfoForm是空的。
using Microsoft.Ccr.Adapters.WinForms;
...
//Create Form
PadInfoForm _form = new PadInfoForm();
WinFormsServicePort.Post(new RunForm(
    delegate()
    {
        return _form;
    }
));
  下个版本将有一个GUI。如果你有建议或者评论,请提出来!

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引