- Kinect for Windows SDK. Часть 1. Сенсор
- Kinect for Windows SDK. Часть 2. Потоки данных
- Kinect for Windows SDK. Часть 3. Функциональные возможности
- [Играем в кубики с Kinect]
- Программа, апорт!
Иногда удивительно, как стремительно идет развитие IT-индустрии. Я помню, как еще сравнительно недавно, на одном из семинаров TechDays, Евгений Марченков показывал первые ролики о проекте Natal. Много ли людей помнит это название? Ведь сейчас проект известен как Kinect!

Сенсор был разработан для консоли Xbox 360, а с февраля 2012 года доступен для персональных компьютеров. Не так давно мне представилась возможность оценить сенсор и написать немного кода.
Думаю, что вы уже прочитали предыдущие статьи цикла посвященные обзору SDK. Настало время попробовать Kinect в действии! В статье я расскажу о том, как можно написать небольшую игру в кубики. И конечно кубики будут двигаться при помощи рук!
Итак, в первую очередь нам понадобится SDK для сенсора, его можно скачать с официального сайта. В SDK вы найдете некоторое количество примеров и документацию. Так же рекомендую посмотреть серию Quick Starts от Coding4Fun. И на закуску Kinect Paint на codeplex, можно скачивать, компилировать, изучать код.
На что точно не хочется отвлекаться так это на физику в игре. Но без неё тоже как-то грустно. Решение очень простое — использовать готовую библиотеку. Для Silverlight и Windows Phone существует хороший проект Physics Helper. К сожалению, текущая версия (4.0) не поддерживает WPF (ко��ментарии автора по этому поводу здесь), но предыдущая версия поддерживала. Поэтому можно взять исходный код той версии, которая поддерживала WPF, обновить проекты до .NET 4.0 и скомпилировать сборки. Ссылки на обновленный проект в конце статьи.
Теперь в Visual Studio 2010 создаем новый проект WPF Application (C#). В примерах SDK есть проект Microsoft.Samples.Kinect.WpfViewers. В нём можно найти готовые элементы управления для работы с камерами и микрофонами сенсора (viewers), а так же элемент инкапсулирующий логику соединения с сенсором (chooser). Добавим этот проект в наш solution. Вот что получилось:

В окне Toolbox доступны элементы управления из WpfViewers.

Теперь в MainWindows.xaml заменим корневой Grid на Canvas, на нём будем рисовать кубики. Поместим на форму элементы KinectSensorChooser и KinectColorViewer, чтобы соединиться с сенсором и отобразить видеопоток с камеры.

Обратите внимание на binding. Необходимо связать свойство Kinect класса KinectColorViewer со свойством Kinect класса KinectSensorChooser. Так color viewer будет знать, с каким сенсором мы работаем в данный момент. Остается только добавить инициализацию нужных потоков. Это можно сделать в обработчике события KinectSensorChanged (класс KinectSensorChooser), что позволит инициализировать сенсор при подключении. В общем виде это делается так:
sensor.ColorStream.Enable(); // включаем видеопоток
sensor.SkeletonFrameReady += SkeletonsReady; // подписываемся на событие, чтобы знать когда готов кадр с координатами найденных фигур людей
sensor.SkeletonStream.Enable(); // включаем поток
sensor.Start(); // включаем сенсор
Методы Enable потоков перегружены, вы можете передавать в них параметры, изменяющие поведение по умолчанию. Так перегруженный метод Enable для ColorStream принимает значение перечисления (enum) ColorImageFormat для настройки формата изображения (RGB или YUV), а так же его разрешения.
Технически этого достаточно, чтобы запустить приложение и увидеть видео того, что попадает в поле зрения Kinect.
Для использования физики в игре в первую очередь добавим в проект ссылки на сборки: FarseerPhysics, Spritehand.FarseerHelper и Spritehand.PhysicsBehaviors из библиотеки Physics Helper.Следующее, что необходимо сделать — добавить к корневому элементу Canvas поведение (behavior) PhysicsControllerBehavior. Теперь нарисуем прямоугольник – это будет пол или земля, как угодно. Добавим ему поведение физического объекта PhysicsObjectBehavior и установим свойство IsStatic равным true. Я не буду останавливаться подробно на использовании Physics Helper, вы можете посмотреть видеоурок на странице библиотеки. Кубики можно задать в xaml разметке, а можно в C# коде. Главное не забыть о необходимости физического поведения для свежесозданного кубика.
private void CreateCube()
{
var cube = new Rectangle
{
Name = string.Format("PART_Cube_{0}", Guid.NewGuid().ToString("N")),
Width = CubeSide,
Height = CubeSide,
Fill = new SolidColorBrush(Colors.Red),
Stroke = new SolidColorBrush(Colors.Black),
StrokeThickness = 3,
RadiusX = CubeCornerRadius,
RadiusY = CubeCornerRadius
};
var behavior = new PhysicsObjectBehavior { IsStatic = false };
behavior.Attach(cube);
PART_LayoutRoot.Children.Add(cube);
}
Прежде чем двигаться дальше, создадим курсор, который будет отображать движение руки по полю. Можно взять какую-нибудь готовую картинку или красиво нарисовать курсор с помощью элемента Path. Я не стал брать картинку или красиво рисовать Path’ом, а пошел третьим путем и нарисовал Path’ом некрасиво.

Зияющая красная дырка вовсе не рана, а просто указатель центра, назначение которого я опишу чуть дальше.
Теперь остается только обрабатывать событие SkeletonFrameReady и перемещать «чудо-руку» по полю. Общий подход обработки события таков: получить кадр, вытащить из него данные по всем человеческим фигурам (skeleton) найденным в кадре и среди этих фигур выбрать первую отслеживаемую (tracked). Kinect может предоставлять данные о шести фигурах в кадре, но отслеживать перемещение может только двух (см. Player Segmentation Data). Для простой игры нам достаточно получить первого отслеживаемого игрока:
private Skeleton GetTrackedSkeleton(SkeletonFrameReadyEventArgs frameReadyEventArgs)
{
using (SkeletonFrame skeletonFrameData = frameReadyEventArgs.OpenSkeletonFrame())
{
if (skeletonFrameData == null)
{
return null;
}
Skeleton[] allSkeletons = new Skeleton[skeletonFrameData.SkeletonArrayLength];
skeletonFrameData.CopySkeletonDataTo(allSkeletons);
return allSkeletons.Where(s => s.TrackingState == SkeletonTrackingState.Tracked).FirstOrDefault();
}
}
Информация о человеческой фигуре (skeleton) это набор из 20 точек. Изображение витрувианского человека как нельзя лучше подходит для их демонстрации.

Итак, из skeleton мы можем получить позицию интересующей нас части тела игрока и обновить игровое поле, если необходимо. Примем следующую систему жестов:
- если левая рука игрока поднята и правая рука находится над кубиком – кубик перемещается вместе с правой рукой (перетаскивается);
- если левая рука опущена, то правая может спокойно перемещаться над игровым полем.
Очень просто проверить положение левой руки. Достаточно сравнить Y-координаты кисти левой руки и левого плеча:
private bool IsAcionMode(JointCollection joints)
{
Joint lh = joints[JointType.HandLeft];
Joint ls = joints[JointType.ShoulderLeft];
return lh.Position.Y > ls.Position.Y;
}
Для того чтобы определить находиться ли правая рука игрока над кубиком, можно использовать функцию HitTest класса VisualTreeHelper. Помните красный кружок в «чудо-руке»? Именно под этой точкой мы ищем кубик.
Функция обработки жестов:
private void UpdateScenaItems(Skeleton skeleton)
{
Point currentPos = skeleton.Joints[JointType.HandRight]
.ScaleTo((int)PART_LayoutRoot.ActualWidth, (int)PART_LayoutRoot.ActualHeight, 0.50f, 0.20f)
.Position
.ToPoint2D();
Rectangle cube = GetCubeUnderCursor(currentPos);
var isAction = IsAcionMode(skeleton.Joints);
if (!isAction || (isAction && cube == null))
{
UpdateCursorPosition(currentPos);
return;
}
MoveCubeUnderCursor(cube, currentPos);
}
Метод расширения ScaleTo используется для приведения координат к масштабу игрового поля. Это метод входит в состав пакета Coding4Fun Kinect Toolkit (доступен для скачивания через NuGet). ToPoint2D — мой метод расширения использующийся для конвертации SkeletonPoint в Point. Понятно, что в методе UpdateCursorPosition мы перемещаем только курсор, а в методе MoveCubeUnderCursor перетаскиваем кубик.
Теперь можно запустить игру и немного помахать руками!
Ниже вы найдете исходные коды и сборки(assemblies) игры.
P.S. Время от времени кубики слипаются или застревают в полу – забавно.
P.P.S. В следующей статье я расскажу о том, как научить приложение понимать голосовые команды.
Сборки игры
Исходные коды игры
Сборки библиотеки PhysicsHelper
Исходные коды библиотеки PhysicsHelper