inectはカメラ角度を-27~+27度まで変更できる機能があります。
ユーザはさまざまな動作をします。ユーザの動きに合わせてカメラ角度を自動調整させます。
今回は、分かりやすいように、左手に追従して上下のカメラ角度を変更します。
●ソース
using System; using System.Collections.Generic; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Audio; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.GamerServices; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Microsoft.Xna.Framework.Media; using Microsoft.Research.Kinect.Nui; //Kinect Uniの読み込み using System.Threading; namespace WindowsGame6 { /// <summary> /// 基底 Game クラスから派生した、ゲームのメイン クラスです。 /// </summary> public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Runtime nui; //Kinectのセンサクラス int Angle=0; //首角度 int Back_Angel =0; //首角度 一つ前の Vector3 HandLeft_P = new Vector3(); //左手の座標 Thread Thread_Angle; //スレッド Texture2D texture_depth = null; //奥行テクスチャ private Color[] depthColor; //色情報の格納 public Game1(){ graphics = new GraphicsDeviceManager(this); Content.RootDirectory = "Content"; } protected override void Initialize(){ base.Initialize(); nui = new Runtime(); //Kinectセンサクラスの初期化 try{ //奥行の取得、トラッキング、実画像 nui.Initialize( RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor); } catch (InvalidOperationException){ Console.WriteLine("Runtime initialization failed."); return; } try{ //デプスストリームを開く nui.DepthStream.Open( ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex); }catch (InvalidOperationException){ Console.WriteLine("Failed to open stream. "); return; } //ビデオ更新毎にnui_DepthFrameReadyを呼び出す nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady); //フレーム更新毎にnui_SkeletonFrameReadyを呼び出す nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady); this.Thread_Angle = new Thread(Change); //スレッドの初期化 } protected override void LoadContent(){ spriteBatch = new SpriteBatch(GraphicsDevice); } protected override void UnloadContent(){ } protected override void Update(GameTime gameTime){ if(GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit(); try{ if(HandLeft_P.Y !=0){ this.Angle = nui.NuiCamera.ElevationAngle; int l= (int)Math.Abs(this.HandLeft_P.Y -120)/12; if(l>=2){ if(this.HandLeft_P.Y >120){ this.Angle-=l*2; }else{ this.Angle+=l*2; } } } //正規化 -27 ~ 27 に収まるように if(this.Angle >= 27){ this.Angle= 27; } if(this.Angle <= -27){ this.Angle=-27; } if(this.Back_Angel != this.Angle){ //既にスレッドが動いていないときに実行 if(this.Thread_Angle.IsAlive !=true){ this.Thread_Angle = new Thread(Change); //スレッドスタート this.Thread_Angle.Start(); this.Back_Angel=this.Angle; } } }catch( InvalidOperationException e){ } base.Update(gameTime); } #region 首振りヘッドスレッド public void Change(){ try{ nui.NuiCamera.ElevationAngle=this.Angle; //首を振る Thread.Sleep(500); //スリープ }catch{} } #endregion protected override void Draw(GameTime gameTime){ GraphicsDevice.Clear(Color.CornflowerBlue); this.spriteBatch.Begin(); //奥行画像の描写 if(this.texture_depth !=null){ //奥行きTextureの描写 this.spriteBatch.Draw(this.texture_depth,new Vector2(0,0), null, Color.White,0.0f,Vector2.Zero,1.0f,SpriteEffects.None,0.0f); } this.spriteBatch.End(); base.Draw(gameTime); } #region 奥行き画像 void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e){ lock(this){ PlanarImage Image = e.ImageFrame.Image; // byte[] convertedDepthFrame = convertDepthFrame(Image.Bits); int no=0; this.depthColor = new Color[Image.Height * Image.Width]; this.texture_depth = new Texture2D(graphics.GraphicsDevice , Image.Width, Image.Height); //テクスチャの作成 //画像取得 for (int y = 0; y < Image.Height; ++y){ //y軸 for (int x = 0; x < Image.Width; ++x, no += 2){ //x軸 int n= (y* Image.Width +x)*2; int realDepth = (Image.Bits[n +1 ] <<5 )|(Image.Bits[ n ] >>3 ); byte intensity = (byte)((255 - (255 * realDepth / 0x0fff))/2); this.depthColor[ y* Image.Width +x ] = new Color( intensity,intensity,intensity ); } } //左手の位置に赤ドットを打つ int px = (int)this.HandLeft_P.X; int py = (int)this.HandLeft_P.Y; //px,pyがdepthColorの範囲外だとエラーが発生するので注意 depthColor[ py * Image.Width + px ] = Color.Red; this.texture_depth.SetData(this.depthColor); //texture_imageにデータを書き込む } } #endregion #region スケルトン void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e){ SkeletonFrame skeletonFrame = e.SkeletonFrame; foreach (SkeletonData data in skeletonFrame.Skeletons){ if (SkeletonTrackingState.Tracked == data.TrackingState){ foreach (Joint joint in data.Joints){ //左手の座標取得 if(joint.ID == JointID.HandLeft){ nui.SkeletonEngine.SkeletonToDepthImage( joint.Position, out this.HandLeft_P.X, out this.HandLeft_P.Y); //座標変換 //convert to 320, 240 space HandLeft_P.X = Math.Max(0, Math.Min(HandLeft_P.X * 320, 320)); HandLeft_P.Y = Math.Max(0, Math.Min(HandLeft_P.Y * 240, 240)); } } } } } #endregion } }
●実行結果
今回は、左手でしたが、これを体の中心座標や、頭の座標に設定すれば、激しい動きにも追従することができます。

