- PVSM.RU - https://www.pvsm.ru -
Иногда удивительно, как стремительно идет развитие IT-индустрии. Я помню, как еще сравнительно недавно, на одном из семинаров TechDays, Евгений Марченков [1] показывал первые ролики [2] о проекте Natal. Много ли людей помнит это название? Ведь сейчас проект известен как Kinect!
Сенсор был разработан для консоли Xbox 360, а с февраля 2012 года доступен для персональных компьютеров. Не так давно мне представилась возможность оценить сенсор и написать немного кода.
В статье я расскажу о том, как можно написать небольшую игру в кубики. И конечно кубики будут двигаться при помощи рук!
Итак, в первую очередь нам понадобится SDK для сенсора, его можно скачать с официального сайта [3]. В SDK вы найдете некоторое количество примеров и документацию. Так же рекомендую посмотреть серию Quick Starts [4] от Coding4Fun. И на закуску Kinect Paint [5] на codeplex, можно скачивать, компилировать, изучать код.
На что точно не хочется отвлекаться так это на физику в игре. Но без неё тоже как-то грустно. Решение очень простое — использовать готовую библиотеку. Для Silverlight и Windows Phone существует хороший проект Physics Helper [6]. К сожалению, текущая версия (4.0) не поддерживает WPF (комментарии автора по этому поводу здесь [7]), но предыдущая версия поддерживала. Поэтому можно взять исходный код той версии, которая поддерживала 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(); // включаем сенсор
* This source code was highlighted with Source Code Highlighter [9].
Методы Enable потоков перегружены, вы можете передавать в них параметры, изменяющие поведение по умолчанию. Так перегруженный метод Enable [10] для ColorStream принимает значение перечисления (enum) ColorImageFormat [11] для настройки формата изображения (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);
}
* This source code was highlighted with Source Code Highlighter [9].
Прежде чем двигаться дальше, создадим курсор, который будет отображать движение руки по полю. Можно взять какую-нибудь готовую картинку или красиво нарисовать курсор с помощью элемента Path [12]. Я не стал брать картинку или красиво рисовать Path’ом, а пошел третьим путем и нарисовал Path’ом некрасиво.
Зияющая красная дырка вовсе не рана, а просто указатель центра, назначение которого я опишу чуть дальше.
Теперь остается только обрабатывать событие SkeletonFrameReady [13] и перемещать «чудо-руку» по полю. Общий подход обработки события таков: получить кадр, вытащить из него данные по всем человеческим фигурам (skeleton) найденным в кадре и среди этих фигур выбрать первую отслеживаемую (tracked). Kinect может предоставлять данные о шести фигурах в кадре, но отслеживать перемещение может только двух (см. Player Segmentation Data [14]). Для простой игры нам достаточно получить первого отслеживаемого игрока:
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();
}
}
* This source code was highlighted with Source Code Highlighter [9].
Информация о человеческой фигуре (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;
}
* This source code was highlighted with Source Code Highlighter [9].
Для того чтобы определить находиться ли правая рука игрока над кубиком, можно использовать функцию HitTest [15] класса 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);
}
* This source code was highlighted with Source Code Highlighter [9].
Метод расширения ScaleTo используется для приведения координат к масштабу игрового поля. Это метод входит в состав пакета Coding4Fun Kinect Toolkit (доступен для скачивания через NuGet). ToPoint2D — мой метод расширения использующийся для конвертации SkeletonPoint [16] в Point [17]. Понятно, что в методе UpdateCursorPosition мы перемещаем только курсор, а в методе MoveCubeUnderCursor перетаскиваем кубик.
Теперь можно запустить игру и немного помахать руками!
Ниже вы найдете исходные коды и сборки(assemblies) игры.
P.S. Время от времени кубики слипаются или застревают в полу – забавно.
P.P.S. В следующей статье я расскажу о том, как научить приложение понимать голосовые команды.
Сборки игры [18]
Исходные коды игры [19]
Сборки библиотеки PhysicsHelper [20]
Исходные коды библиотеки PhysicsHelper [21]
Автор: Lardite
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/programmirovanie/5635
Ссылки в тексте:
[1] Евгений Марченков: http://www.techdays.ru/speaker/emarchenkov
[2] ролики: http://www.youtube.com/watch?v=g_txF7iETX0
[3] официального сайта: http://www.kinectforwindows.org/
[4] Quick Starts: http://channel9.msdn.com/Series/KinectQuickstart
[5] Kinect Paint: http://paint.codeplex.com/
[6] Physics Helper: http://physicshelper.codeplex.com/
[7] здесь: http://physicshelper.codeplex.com/discussions/251231
[8] Image: http://3.bp.blogspot.com/-4yTIrrEfp4E/T4njrYciPQI/AAAAAAAAADg/1CueI_-c1fw/s1600/VisualStudio.png
[9] Source Code Highlighter: http://virtser.net/blog/post/source-code-highlighter.aspx
[10] Enable: http://msdn.microsoft.com/en-us/library/microsoft.kinect.colorimagestream.enable.aspx
[11] ColorImageFormat: http://msdn.microsoft.com/en-us/library/microsoft.kinect.colorimageformat.aspx
[12] Path: http://msdn.microsoft.com/en-us/library/system.windows.shapes.path.aspx
[13] SkeletonFrameReady: http://msdn.microsoft.com/en-us/library/microsoft.kinect.kinectsensor.skeletonframeready.aspx
[14] Player Segmentation Data: http://msdn.microsoft.com/en-us/library/hh855352.aspx#ID4EMAAC
[15] HitTest: http://msdn.microsoft.com/en-us/library/ms608753%28v=vs.100%29.aspx
[16] SkeletonPoint: http://msdn.microsoft.com/en-us/library/microsoft.kinect.skeletonpoint.aspx
[17] Point: http://msdn.microsoft.com/en-us/library/system.windows.point.aspx
[18] Сборки игры: https://sites.google.com/site/larditefiles/KinectPhysicsCubes.Bin.zip
[19] Исходные коды игры: https://sites.google.com/site/larditefiles/KinectPhysicsCubes.Src.zip
[20] Сборки библиотеки PhysicsHelper: https://sites.google.com/site/larditefiles/PhysicsHelper.Bin.zip
[21] Исходные коды библиотеки PhysicsHelper: https://sites.google.com/site/larditefiles/PhysicsHelper.Src.zip
Нажмите здесь для печати.