Работа с игровыми контроллерами


    Приветствую, дорогие читатели!

    В одном из проектов мне понадобилось работать с игровыми контроллерами, в частности с игровым рулем. Было необходимо получить состояние руля – угол поворота, нажатые кнопки. Создавалась не игра, это было обычное .net-приложение, не буду вдаваться в подробности проекта. Речь пойдет о том, как получить информацию о состоянии игровых контроллеров в .net.

    Предметная область


    Задача – получить данные от игрового руля. В первую очередь необходимо изучить предметную область. Где используются игровые рули? Очевидно, в играх. Множество игр разрабатывается с помощью технологии DirectX. Почитав википедию, можно узнать, что DirectX подразделяется на множество интерфейсов. Интерес для нас представляет интерфейс DirectInput.

    DirectInput — интерфейс, используемый для обработки данных, поступающих с клавиатуры, мыши, джойстика и пр. игровых контроллеров. (с) Wikipedia.org

    Используемые игровые контроллеры


    Для реализации проекта в мое распоряжение был предоставлен игровой руль Defender Forsage GTR.
    Также я не упустил возможности поэкспериментировать со своим игровым джойстиком Logitech Rumblepad 2.

    Managed DirectX


    Как Вы, наверное, догадались Managed DirectX — это поддержка DirectX из управляемого кода, т.е. из программ, написанных с использованием .net. MDX включен в состав DirectX SDK, начиная с девятой версии.
    Если Вы заинтересовались MDX, то можете почитать книгу Тома Миллера «DirectX 9 с управляемым кодом. Программирование игр и графика». В сети есть масса информации по теме.

    Необходимые инструменты


    Логично, что в первую очередь необходимо инсталлировать драйвер производителя игрового контроллера, хотя, как выяснилось, это не является обязательным пунктом, т.к. в ходе моих экспериментов с джойстиком Logitech, никаких драйверов для него я не инсталлировал, и он стабильно отвечал на мои запросы (под управлением ОС Windows 7).
    Мы используем DirectX, поэтому нам понадобится DirectX SDK, скачать его можно здесь. Все необходимое для работы с .net там уже есть. В качестве IDE я использовал Visual Studio 2010.

    Начало работы


    Итак, DirectX SDK установлен, запустим Visual Studio, создадим новый проект и подключим к нему Managed DirectX. Для этого идем в References -> Add Reference, идем во вкладку Browse, идем в папку \Windows\Microsoft.NET\DirectX for Managed Code\1.0.2902.0 и подключаем к проекту Microsoft.DirectX.dll и Microsoft.DirectX.DirectInput.dll, соответственно не забываем про:

    using Microsoft.DirectX;
    using Microsoft.DirectX.DirectInput;
    

    Конечно, не обошлось без подводных камней. В своих проектах в основном я использую .net framework 4.0, а у MDX проблемы с этой версией фреймворка, и на этапе сборки проекта Visual Studio 2010 просто зависнет. Поэтому у нас есть два пути решения проблемы:

    1) использовать более раннюю версию .net framework.
    2) немного подправить конфиги проекта, что мы и сделаем.

    Добавим к проекту новый файл app.config со следующим содержимым:

    <?xml version="1.0"?>
    <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0,Profile=Client"/>
      </startup>
    </configuration>
    

    Теперь все готово, можно начинать работать с игровыми контроллерами.

    Получение состояния игрового руля


    Очевидно, что первым делом необходимо узнать обо всех подключенных игровых контроллерах:

    foreach (DeviceInstance instance in Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly))
    {
         // На этом этапе можно узнать информацию об игровом контроллере, например, название:
         instance.ProductName
    }
    

    Далее необходимо инициализировать игровое устройство. Для наглядности подразумеваю, что оно у меня одно:

    Device device;
    
    foreach (DeviceInstance instance in Manager.GetDevices(DeviceClass.GameControl, EnumDevicesFlags.AttachedOnly))
    {
         device = new Device(instance.ProductGuid);
         // Background - флаг, говорит о том, что данные от руля будут поступать даже в неактивное окно
         // NonExclusive - говорит о том, что игровой контроллер могут использовать и другие приложения
         device.SetCooperativeLevel(null, CooperativeLevelFlags.Background | CooperativeLevelFlags.NonExclusive);
    
         // Зададим дополнительные параметры
         foreach (DeviceObjectInstance doi in device.Objects)
         {
              // Проверяем есть ли на устройстве что-нибудь поворачивающееся
              if ((doi.ObjectId & (int)DeviceObjectTypeFlags.Axis) != 0)
              {
                   // Задаем минимальное и максимальное значение угла поворота
                   device.Properties.SetRange(
                              ParameterHow.ById,
                              doi.ObjectId,
                              new InputRange(-90, 90));
               }
         }
    
         // Применяем настройки
         device.Acquire();
    }
    

    В характеристиках руля сказано, что угол поворота рулевого колеса составляет 180 градусов, поэтому в InputRange я указал значение угла отклонения влево и вправо в 90 градусов.

    К сожалению, мы не сможем подписаться на какие-либо события от руля или джойстика вроде KeyDown\KeyUp, и состояние игрового контроллера придется опрашивать вручную:

    // Создаем таймер
    DispatcherTimer timer = new DispatcherTimer();
    
    timer.Tick += new EventHandler(timer_Tick);
    timer.Interval = new TimeSpan(0, 0, 0, 0, 10);
    timer.Start();
    
    private void  timer_Tick(object sender, EventArgs e)
    {
         // Получим текущее состояние руля
         JoystickState j = device.CurrentJoystickState;
    
         string info = "";
    
         // Собираем информацию о кнопках
         byte[] buttons = j.GetButtons();
         for (int i = 0; i < buttons.Length; i++)
         {
              // Узнаем какие из кнопок нажаты на данный момент
              if (buttons[i] != 0)
              {
                   info += "Button: " + i + " ";
              }
         }
    
         // j.X будет содержать информацию об угле поворота руля
         textBlock1.Text = j.ToString();
         textBlock2.Text = info;
    }
    

    Для наглядности можно вывести на экран структуру JoystickState и увидеть, какие поля меняются в зависимости от состояния игрового руля. Все это справедливо также и для джойстика.

    Выводы


    Оказывается, запрограммировать игровой контроллер под свои нужды не так уж и сложно, большинство всевозможных джойстиков\рулей и т.п. подчиняются интерфейсу DirectInput, теперь в свои проекты\игры вы с легкостью сможете добавить их поддержку. Что касается игр, то остается только реализовать функционал, дающий пользователю возможность самому «навешивать» на кнопки действия, предусмотренные логикой игры.

    Ссылки


    1. DirectX Software Development Kit June 2010 \ 572 Mb
    2. Том Миллер — Managed DirectX 9 с управляемым кодом
    3. Исходники демонстрационного проекта

    Спасибо за внимание.
    • +23
    • 16,6k
    • 6
    Поделиться публикацией

    Комментарии 6

      +4
      никаких драйверов для него я не инсталлировал, и он стабильно отвечал на мои запросы (под управлением ОС Windows 7).

      И это логично, ибо почти все джойстики являются HID (Human interface device) и для них подходит стандартный драйвер, ибо общение с HID стандартизировано (передаётся количество осей, кнопок и их состояния).
        +1
        Помню в начале века писал программу для работы с игровым портом для доса на турбо-паскале. Причем применение было тоже не совсем игровое. К разъему игрового порта звуковой платы я подключал магнитно-контактные извещатели для макетирования простейшей охранной системы. Нажатия/отпускание «кнопок» соответствовало размыканию/замыканию контактов. Контроль целостности самих шлейфов определялся на аналоговом входе через измерение изменения сопротивления цепи.
          +5
          Немного оффтоп, но Managed DirectX уже весьма длительное время deprecated, Microsoft его не развивает и взамен предлагает XNA. Мне самому в Managed DirectX попадалось пару багов, к примеру при отрисовке sprite font при некоторых параметрах происходит утечка памяти (причем безвозвратная), которые невозможно искоренить в силу закрытости исходных кодов. Для новых проектов могу порекомендовать SlimDX.
            0
            Считаю если покупать руль — то уж Логитек, а не дефендеры с гениусами. И к ним есть замечательная утилита, где в GUI настраивается даже больше чем оно нужно.
            Применение утилиты Logitech Profiler
              0
              Может конечно я очень резко насчет производителей, погорячился.
              0
              Низкий поклон шлемом об асфальт! Как раз стоит на данный момент подобная задачка — статья прям в тему!

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое