張子の虎をキー・タップすると頭が上下に動くLeap Motionプログラムを作る
今回紹介するLeap Motionアプリは、画面に表示した“張り子の虎”の頭の部分をキー・タップ(あたかも指でキーを押しているように「下方向」にタップ)すると、頭が上下に揺れるアプリです。早速開発手順を見ていきましょう。以下VS2012と記述している個所はVS2013と読み変えても問題はありません。
まずWPFプロジェクトを作成しよう
今回のLeap MotionアプリもWPFで作成します。
これには、Visual Studio 2012(以下、VS 2012)のIDEを起動して、メニューバーから[ファイル]−[新規作成]−[プロジェクト]と選択して、それにより表示される[新しいプロジェクト]ダイアログで「Visual Basic」のテンプレートから「WPF アプリケーション」を選択します。
※Leap Motionは、.NET Framework 3.5と4.0に対応しています。しかし、.NET Framework 4.5でも動作します。今回のアプリは全て.NET Framework 4.5で作成しています。しかし、あくまでも対応しているのは、.NET Framework 3.5と4.0です。心配な方は、.NET Framework 4.0で作成すると安心でしょう。
[名前]欄には、ここでは「ToraTapLeapMotion」と指定します。
ソリューション・エクスプローラー内にImagesというフォルダを作成して、「虎の頭の部分」と「虎の身体の部分」の2つの透明化されたPNG画像を配置しておきます。
ダウンロードされたサンプルファイルには、これらの画像ファイルは追加済みです。
WPFの基本的な作成手順は、「画面上の図形を5本の指で操作する基本的なLeap Motionプログラムを作る」と同じ手順となるので、説明を割愛します。具体的な手順は、第1回の「参照の追加」「プロジェクトのルートに「LeapCSharp.dll」と「Leapd.dll」を追加する」「プロパティを設定する」を参考にしてください。
今回のLeap Motionアプリについて
今回のアプリは、張り子の虎の頭部分をキー・タップすると、頭が上下に揺れるアプリです(図1参照)。
キー・タップにはコツがいりますので、最初はうまく動かない可能性があります。1本の指で虎の頭をチョンチョンと2回ほどつつくような動作をしてください。タッチ・ポイントの色が赤に変わってからつつく動作をします。
実際に動かした動画は下記になります。
今回は、「ポーリング方式」と「コールバック方式」の混合を用いています。「コールバック方式」については、第1回のサンプルの「コールバック方式の採用」を参照してください。「ポーリング方式」については4回の「ポーリング方式の採用」を参照してください。
またコールバックメソッド等については、「C#開発者から見たLeap Motion開発のファースト・インプレッション」を参照してください。
画面のレイアウト(MainWindow.xaml)
Canvas内に名前が「head」というImageコントロールと、名前が「body」というImageコントロールを配置し、それぞれのSourceプロパティにImagesフォルダ内にある虎の「頭」と「身体」の部分の画像を読み込んでおきます。
一番前面に、タッチ・ポイントを表示するpaintCanvasという名前のInkPresenterコントロールを配置します(リスト1)。
リスト1 書き出されたMainWindow.xaml
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Canvas> <Image x:Name="head" HorizontalAlignment="Left" Height="54" VerticalAlignment="Top" Width="65" Source="Images/虎頭.png" Canvas.Left="184" Canvas.Top="147"/> <Image x:Name="body" HorizontalAlignment="Left" Height="134" VerticalAlignment="Top" Width="172" Source="Images/虎身体.png" Canvas.Left="148" Canvas.Top="102"/> <InkPresenter Name="paintCanvas"/> </Canvas> </Window>
レイアウトは図2のようになります。
Blendを起動してストーリーボードを作成
次に、アニメーションのためのストーリーボードを作成します。
まずはBlendを起動します。実際にBlend起動する前に、必ずVS 2012でプロジェクトをビルドしておきます。
VS 2012からBlendを直接起動する方法もありますが、今回は.exeファイルを実行して起動します。64bitのWindows 8環境であれば、「C:\Program Files (x86)\Microsoft Visual Studio 11.0\Blend\Blend.exe」を実行してください。なお、VS 2012をインストールすると、Blendも自動的にインストールされています。Windows 8のスタート画面にも表示されているはずですから、それをタップして起動しても構いません。
Blendが起動したら、[プロジェクトを開く]から、現在作成している「TotaTapLeapMotion.sln」ファイルを指定して開きます。
「Storybord1」というストーリーボードを作成します(作成手順は「MSDN: ストーリーボードの作成、変更、または削除」を参照してください)。
アートボード全体が赤い枠線で囲まれてストーリーボードの記録が可能になるので、[オブジェクトとタイムライン]で「head」を選択して、黄色の再生ヘッドが「0.5」の位置で、headプロパティの[変換]パネル内にある[平行移動]の「x」に「2」、「y」に「10」を指定し、同じく、[傾斜]の「x」に「0」、「y」に「-20」と指定します。次に黄色の再生ヘッドを「1」の位置で、[変換パネル]内の[平行移動]の「x」に「5」、「y」に「3」と指定します。[傾斜]の「x」と「y」には0を指定します。最後に記録された楕円のマークを選択して、「イージング」プロパティを指定します。BounceInを選択し、Bouncesに「2」、Bouncinessに「1」を指定します。この辺りの数値は、タイムラインから再生をしてみて、適当な値に設定してください(図3参照)。
Blendを閉じて、VS 2012に戻ります。保存と適用のメッセージが出ますので、保存して適用させます。
またストーリーボードのコードの下に、次のリスト2のコードが追加されていますので、このコードは削除します。このコードを残したままにしておくと、アプリを実行した際に、即、ストーリーボードが実行されてしまいます。
リスト2(MainWindow.xaml) 削除するコード
<Window.Triggers> <EventTrigger RoutedEvent="FrameworkElement.Loaded"> <BeginStoryboard Storyboard="{StaticResource Storyboard1}"/> </EventTrigger> </Window.Triggers>
プログラム・コード(MainWindow.xaml.vb)
では、次にプログラム・コード(MainWindows.xaml.vbファイル)を見ていきましょう。
コードは4回のコードと似通っている部分については割愛します。4回のコード説明を参照してください。異なる部分のみ解説します。
名前空間の読み込み
「Imports Leap」と「Imports System.Windows.Ink」名前空間の他に、Storyboardを使用するため「Imports System.Windows.Media.Animation」を読み込んでおきます。
メンバー変数の宣言
次にメンバー変数を宣言します。
「leap」、「touchIndicator」、「touchPoint」、「windowWidth」、「windowHeight」、「x」、「y」、「tx」、「ty」、「FingersCount」等のメンバー変数については4回と同じのため、そちらを参照してください。但し今回は、windowWidthを525、windowHeightを350で初期化しています。
モジュールの作成
VS2012メニューから[プロジェクト]−[モジュールの追加]と選択して、[名前]が「Module1.vb」という名前のモジュールを作成します(リスト3)。
リスト3 (Modeule1.vb)
Imports System.Windows.Media.Animation Module Module1 Public Index As Integer Public strb As Storyboard End Module
MainWindow_Loadedメソッドの処理
4回と同じにつき割愛します、該当記事を参照してください。
Updateメソッドの処理
先頭に、
strb = TryCast(Me.Resources("Storyboard1"), Storyboard) ' strbに「Storyboard1」を取得して格納する
と記述し、モジュール変数strbに「Storyboard1」を取得して格納しておきます。
次にLeap Motionのタッチ処理になります。タッチのイメージは次の図4のようなイメージです。
Updateメソッドの処理(ホバー処理)
まずホバーの場合は、表示されている円がBlueの色になります。タッチ・ポイントの位置をメンバー変数「x」と「y」に格納します。
画面に表示されている指の数をleap.Frame.Fingers.Countプロパティで取得して、メンバー変数「FingersCount」に格納しておきます(リスト3)。
リスト3 ホバー時の処理(MainWindow.xaml.vb)
For Each Pointable As Pointable In leap.Frame.Pointables ……コード略(前述)…… If Pointable.TouchDistance > 0 AndAlso Pointable.TouchZone <> Global.Leap.Pointable.Zone.ZONENONE Then touchIndicator.Color = Colors.Blue x = touchPoint.X y = touchPoint.Y Index=0 FingersCount = leap.Frame.Fingers.Count' 表示されている指の本数を取得して、メンバー変数FingersCountに格納しておく ……コード略(続きは後述)…… End If Next
次にタッチした処理になります。タッチした場合は、表示されている円が赤に変わります。
Updateメソッドの処理(タッチ処理)
指が1本認識されている場合は、虎の頭(head)部分の座標値を取得し、モジュール変数Indexを1で初期化します。その他の場合はIndexを0で初期化しストーリーボードを停止します(リスト4)。
リスト4 タッチとタッチ以外の処理(MainWindow.xaml.vb)
If Pointable.TouchDistance > 0 AndAlso Pointable.TouchZone <> Global.Leap.Pointable.Zone.ZONENONE Then ……コード略(前述)…… ElseIf Pointable.TouchDistance <= 0 Then touchIndicator.Color = Colors.Red If FingersCount = 1 Then If x > head.GetValue(Canvas.LeftProperty) And x < head.GetValue(Canvas.LeftProperty) + head.Width AndAlso y > head.GetValue(Canvas.TopProperty) And y < head.GetValue(Canvas.TopProperty) + head.Height Then ' headの座標値を取得して、Indexを1で初期化する Index = 1 Else Index = 0 strb.Stop() End If End If Else touchIndicator.Color = Colors.Gold Index=0 strb.Stop() End If Next End Sub
ここまでは、ポーリング処理になります。次にコールバック処理を記述します。
クラスの作成(TapDirection.vb)
では、次にクラスを作成していきます。ソリューション・エクスプローラー内のToraTapLeapMotionプロジェクトを選択して、マウスの右クリックで表示されるメニューから[追加]−[クラス]と選択します。表示される画面から[クラス]を選択し、[名前]に[TapDirection.vb] と指定して[追加]ボタンをタップします。
ScreenTapDirection.vbのプログラム・コード
Enum列挙体は値を定義するための入れ物として使用する、特殊なクラスです。列挙体が便利なのはプログラム中に入力候補が自動的に表示される点です。
TapDirection.vb列挙体の中に、Tapという値を入れておきます(リスト5参照)。
リスト5(TapDirection.vb) 列挙体を記述したコード
Public Enum ScreenTapDirection Tap End Enum
クラスの作成(LeapListener.vb)
次に、第1回でSwipeDirection.vbを作成した方法で、LeapListener.vbクラスを作成しますが、第1回のサンプルと同じコードが含まれているので第1回のLeapListener.vbクラスを使用します。
ソリューション・エクスプローラー内で、ToraTapLeapMotionプロジェクトを選択し、マウスの右クリックで表示されるメニューから、[追加]−[既存の項目]と選択して、第1回で作成しておいたLeapListener.vbを取り込みます。
以下、第1回のサンプルのLeapListener.vbと異なる点のみ解説します。
(1)OnConnectコールバックメソッド内で、GestureTypeにTYPEKEYTAPを指定します。
(2)OnFrameコールバックメソッド内で、GestureType.TYPESWIPEの個所を、GestureType.TYPKEYTAPに変更します。
(3)If (swipe.Direction.y > 0) ThenからEnd Ifまでを削除し
If gst.Type = Gesture.GestureType.TYPEKEYTAP Then TapAction(fingers, TapDirection.Tap) End If
と記述します。
(4)デリゲートのコードを「Public Delegate Sub TapEvent(ByVal sd As TapDirection)」と記述します。
(5)「Public Event LeapSwipe As SwipeEvent」の部分を「Public Event LeapTap As TapEvent」に書き換えます。
(6)「ScreenTapAction」メソッド内の「Case SwipeDirection.Up」の部分を「Case TapDirection.Tap」に書き換えます。
(7)「RaiseEvent LeapSwipe(SwipeDirection.Up)」に関する個所を「 RaiseEvent LeapTap(TapDirection.ScreenTap)」に書き換えます。
上記の変更を行った後、リスト6のコードを作成します。
LeapListener.vbのプログラム・コード
リスト6(LeapListener.vb)
・・・・コード略・・・・ Public Overrides Sub OnFrame(ctlr As Controller) ・・・・コード略・・・・ If (Not fingers.IsEmpty) Then Dim gestures As GestureList = currentFrame.Gestures For Each gst As Gesture In gestures ' GestureTypeがTYPEKEYTAPであった場合は、 ' TapActionメソッドを実行する。 If gst.Type = Gesture.GestureType.TYPEKEYTAP Then TapAction(fingers, TapDirection.Tap) End If Next End If End If End Sub ' TapEventデリゲート経由で、LeapTapメソッドを呼び出す。 Public Delegate Sub TapEvent(ByVal sd As TapDirection) Public Event LeapTap As TapEvent ' GestureTypeがTYPEKEYTAPであった場合のTapActionメソッドの処理。 Private Sub TapAction(ByVal fingers As FingerList, ByVal sd As TapDirection) fingersCount = fingers.Count If (fingersCount = 1) Then Select Case sd Case TapDirection.Tap ' RaiseEventでLeapTapイベントを発生させる。 RaiseEvent LeapTap(TapDirection.Tap) Exit Select End Select End If End Sub
クラスの作成(ToraModel.vb)
第1回のサンプルの、SwipeDirection.vbを作成した方法で、ToraModel.vbクラスを作成します。
第1回の「UpDownModel.vb」と同じコードが多いので異なる個所のみ解説します。
(1) 「Private dispatcher = Application.Current.Dispatcher」メンバー変数を宣言しし、ワーカースレッドからUIスレッドに処理を任せます。
(2) Public Sub New内に「AddHandler listener.LeapTap, AddressOf TapAction」を追加します。
上記の変更を行った後、リスト7のコードを作成します。
ToraModel.vbのプログラム・コード
リスト7(ToraModel.vb)
・・・・コード略・・・・ Public Class ToraModel Implements INotifyPropertyChanged ' メンバー変数の宣言 Private ctlr As Controller Private listener As LeapListener Private dispatcher = Application.Current.Dispatcher ' Newメソッド内で、AddHandlerステートメントに、listenerオブジェクトのLeapTap ' イベントに、TapActionベントハンドラ—を追加する。 Public Sub New() ・・・コード略・・・ AddHandler listener.LeapTap, AddressOf TapAction End Sub ・・・・コード略・・・・
TapActionメソッドの処理
TapDirection列挙体の値がTapであった時の処理を記述します。
モジュール変数Indexが1の場合の処理、すなわち、張り子の虎のheadの座標値が取得された場合の処理です。
dispatcher.Invokeを使って、ワーカースレッドからUIスレッドに処理を任せます。
StoryboardをBeginメソッドで開始します。
具体的なコードはリスト8のようになります。
リスト8 張り子の虎の頭をタップしてストーリーボードが実行される処理(TapActionメソッド)
Private Sub TapAction(ByVal sd As TapDirection) Select Case sd Case TapDirection.Tap If Index = 1 Then dispatcher.Invoke(Sub()' dispatcher.Invokeを使って、ワーカースレッドからUIスレッドに処理を任せる。 strb.Begin()' Storyboardを開始する End Sub) End If Exit Select End Select End Sub
MainWindow.xaml内に[ToraTapModel]を取り込む。
まず名前空間として「xmlns:local="clr-namespace:ToraTapLeapMotion"」を定義します。
次に
イージング処理も記述されています(ストーリーボードコード内の太字部分)
リスト9 「ToraModel」を取り込んだMainWindo.xaml
<Window x:Class="MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ToraTapLeapMotion" Title="MainWindow" Height="350" Width="525"> <Window.DataContext> <local:ToraModel/> </Window.DataContext> <Window.Resources> <!--Blendから設定したStoryboadの設定--> <Storyboard x:Key="Storyboard1"> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[1].(SkewTransform.AngleY)" Storyboard.TargetName="head"> <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="-20"/> <EasingDoubleKeyFrame KeyTime="0:0:1" Value="0"> <EasingDoubleKeyFrame.EasingFunction> <BounceEase EasingMode="EaseIn" Bounces="2" Bounciness="1"/> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)" Storyboard.TargetName="head"> <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="2"/> <EasingDoubleKeyFrame KeyTime="0:0:1" Value="5"> <EasingDoubleKeyFrame.EasingFunction> <BounceEase EasingMode="EaseIn" Bounces="2" Bounciness="1"/> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.Y)" Storyboard.TargetName="head"> <EasingDoubleKeyFrame KeyTime="0:0:0.5" Value="10"/> <EasingDoubleKeyFrame KeyTime="0:0:1" Value="3"> <EasingDoubleKeyFrame.EasingFunction> <BounceEase EasingMode="EaseIn" Bounces="2" Bounciness="1"/> </EasingDoubleKeyFrame.EasingFunction> </EasingDoubleKeyFrame> </DoubleAnimationUsingKeyFrames> </Storyboard> </Window.Resources> <Canvas> <Image x:Name="head" HorizontalAlignment="Left" Height="74" VerticalAlignment="Top" Width="90" Source="Images/虎頭.png" Canvas.Left="165" Canvas.Top="128" Stretch="None"> <Image.RenderTransform> <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup> </Image.RenderTransform> </Image> <Image x:Name="body" HorizontalAlignment="Left" Height="182" VerticalAlignment="Top" Width="126" Source="Images/虎身体.png" Canvas.Left="221" Canvas.Top="69" Stretch="None" /> <InkPresenter Name="paintCanvas"/> </Canvas> </Window>
※注意
今回紹介したサンプルコードを動かす際には、「LeapCSharp.NET4.0.dll」や「LeapCSharp.dll」、「Leap.dll」を読者自身のフォルダ内にあるDLLファイルに指定し直さなければ動かない可能性があるので、動かない場合は再指定してください。
張子の虎をキー・タップで動かすLeap Motionプログラム
『新世代モーションコントローラー Leap Motion -Visual Basicによる実践プログラミング-』 第6回のサンプルプログラムです。
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- Leap Motionのスクリーン・タップを使った音声の再生と画像の表示
- 選択した画像を指の動きに合わせて回転させるLeap Motionプログラムを作る
- 画面に並んだ写真が指の動きに反応して回転するLeap Motionプログラムを作る
- 手の動きに合わせて画面上のキャラクターを操作するLeap Motionプログラムを作る
- 画面上の図形を5本の指で操作する基本的なLeap Motionプログラムを作る
- マウス・カーソルで指定した画像の一部を拡大表示するLeap Motionプログラム
- Leap Motionで画像のトリミングと保存を行うためのサンプルプログラムを作る
- 写真を突っついて一覧から削除、並び替えするLeap Motionプログラムを作る
- 好きな写真を指で選んで順番に拡大表示するLeap Motionプログラムを作る
- Leap MotionアプリをWindowsストアに申請して認定させる