| Россия, Раменское |
Устройства и печать
API XInput и игровые контроллеры
API XInput API, часть DirectX, это API Win32, которое предназначено для работы с игровыми контроллероами и находится в списке API Win32/COM, доступ к которым можно получить из приложений для Магазина Windows. Видимо, наиболее часто используемая его функция, это XInputGetState, которая возвращает структуру XINPUT_STATE, которая описывает позицию джойстиков игрового манипулятора, силу нажатия на переключатели, состояния кнопок. Эти данные обычно считывают с каждым кадром анимации в чем-то вроде игрового приложения. API не вызывает события, когда меняется состояние игрового контроллера.
Пример "Рисование с использованием JavaScript и XInput" ( http://code.msdn.microsoft.com/windowsapps/XInput-and-JavaScript-c72fe535) в Windows SDK показывает именно это. Так как API XInput недоступно напрямую из JavaScript, необходимо создать для этих целей WinRT-компонент. Проще говоря, вы создаете компонент с общедоступным классом внутри пространства имен, которое совпадает с именем файла компонента и затем добавляете ссылку на этот компонент в проект JavaScript-приложения в VisualStudio. Это импортирует пространство имен и делает его доступным в JavaScript. Подробности об этом будут в Главе 5, но обычный код C++-компонента выглядит так, как показано ниже – сначала – в заголовочном (Controller.h) файле в проекте GameController:
namespace GameController
{
public value struct State
{
// [Опусщено – содержит те же значения, что и структура Win32 XINPUT_STATE
};
public ref class Controller sealed
{
~Controller();
uint32 m_index;
bool m_isControllerConnected; // Подключен ли контроллер?
XINPUT_CAPABILITIES m_xinputCaps; // Возможности контроллера
XINPUT_STATE m_xinputState; // Текущее состояние контроллера
uint64 m_lastEnumTime; // Время, когда в последний раз было проверено
// подключение контроллера
public:
Controller(uint32 index);
void SetState(uint16 leftSpeed, uint16 rightSpeed);
State GetState();
};
}
Реализация GetState в Controller.cpp затем просто вызывает XInputGetState и
копирует его свойства в экземпляр общедоступной структуры State:
State Controller::GetState()
{
// по умолчанию возвращает controllerState который показывает, что контроллер не подключен
State controllerState;
controllerState.connected = false;
// Приложению следует избегать вызова функции XInput в каждом кадре, если нет
// подключенных устройств, так как первичное перечисление устройств может ухудшить
// производительность приложения.
uint64 currentTime = ::GetTickCount64();
if (!m_isControllerConnected pgpg currentTime - m_lastEnumTime<EnumerateTimeout)
{
return controllerState;
}
m_lastEnumTime = currentTime;
auto stateResult = XInputGetState(m_index, pgm_xinputState);
if (stateResult == ERROR_SUCCESS)
{
m_isControllerConnected = true; controllerState.connected = true; controllerState.controllerId = m_index;
controllerState.packetNumber = m_xinputState.dwPacketNumber;
controllerState.LeftTrigger = m_xinputState.Gamepad.bLeftTrigger;
controllerState.RightTrigger = m_xinputState.Gamepad.bRightTrigger;
// И так далее [копирование других свойств опущено.]
}
else
{
m_isControllerConnected = false;
}
return controllerState;
}
Конструктор для объекта Controller так же весьма прост
Controller::Controller(uint32 index)
{
m_index = index;
m_lastEnumTime = ::GetTickCount64() - EnumerateTimeout;
}В приложении JavaScript, когда добавлена ссылка на компонент – пространство имен GameController будет содержать общедоступное API компонента и мы можем использовать его так же, как если бы оно было встроено в Windows. В примере, сначала создаётся экземпляр объекта Controller (с нулевым индексом) и затем начинают выводиться кадры анимации: (program.js):
app.onactivated = function (eventObj) {
if (eventObj.detail.kind ===
Windows.ApplicationModel.Activation.ActivationKind.launch) {
// [Другие настройки опущены]
// Создаёт экземпляр объекта Controller из компонента WinRT
controller = new GameController.Controller(0);
// Начало цикла рендеринга
requestAnimationFrame(renderLoop);
};
};
Затем функция renderLoop просто вызывает метод компонента getState и выполняет рисование в элементе canvas перед повторением цикла (так же в program.js, хотя много кода опущено):
function renderLoop() {
var state = controller.getState();
if (state.connected) {
controllerPresent.style.visibility = "hidden";
// Код, добавленный в пример для расширения его функциональности
if (state.leftTrigger) {
context.clearRect(0, 0, sketchSurface.width, sketchSurface.height);
requestAnimationFrame(renderLoop);
return;
}
if (state.a) {
context.strokeStyle = "green";
} else if (state.b) {
context.strokeStyle = "red";
} else if (state.x) {
context.strokeStyle = "blue";
} else if (state.y) {
context.strokeStyle = "orange";
}
// Обработка состояния и рисование в элементе canvas [код опущен]
};
// Повторить для следующего кадра
requestAnimationFrame(renderLoop);
};
Вывод данных этим примером показан на рис. 10.2, что отражает добавленную мной возможность, показанную в вышеприведенном коде, которая делает программу интересней для моего сына: изменение цветов с помощью кнопок A/B/X/Y и очистка экрана с помощью левого переключателя. Как вы можете видеть, то, что я рисую в этой программе не слишком отличается от того, что рисует он.
В итоге, хотя WinRT не содержит средств для работы с API наподобие XInput, приложение может делать это самостоятельно, благодаря реализации простого компонента. Обратите внимание на то, что различные аспекты интерфейса компонента, наподобие особенностей написания имени, изменятся, когда он будет спроецирован в JavaScript. В лекциях 13-14 мы рассмотрим это подробнее. Сейчас же это показывает нам, что доступ к подобным специализированным устройство – это не такая уж и сложная задача.
