キャラクターが音声で応援してくれる脳トレーニングアプリを作ってみよう

2014年1月9日(木)
薬師寺 国安

ロジックコードを記述する

※コードが長くなるため、今回のテーマと肝となる「数値をランダムに表示させる」並びに「音声認識」の部分をメインに解説しています。名前空間の読み込み、メンバー変数の宣言も省略しています。全てのコードを見る場合は、サンプルをダウンロードして見てください。

リスト2 (MainWindow.xaml.vb)

‘ RandomInfoというクラス内に、「乱数」という数値型のプロパティを定義しておきます。

Public Class RandomInfo
  Public Property 乱数 As Integer
End Class

ランダムな数値を表示する処理

新しいRandomのインスタンスRndオブジェクトを作成します。
数値型の変数myRndを宣言し0で初期化します。添え字が31の数値型配列変数myArray(31)を宣言します。
RandomInfoクラス型の新しいリストである、myRandomInfo変数を宣言します。
最初に、繰り返し変数iで1から31までを繰り返し、配列変数myArrayに格納しておきます。
同様に、繰り返し変数iで1から31までを繰り返し、以下の処理を行います。

myRnd = Rnd.Next(i, 31)

ここで、最少がiで最大が31までのランダムな数を生成し、変数myRndに格納します。

このままでは、重複した乱数が表示されるため、myRndで初期化された配列変数myArrayをtemp変数に格納します。myRndを配列に持つmyArrayにmyArray(i)の値を代入します。最後にmyArray(i)にtempの値を代入します。これで重複しない1から31までの数値が表示されます。しかし、数値は30までの表示でいいので、繰り返し変数iが31より小さかった時だけ、RandomInfoクラスの「乱数」プロパティにmyArray(i)の値を指定して、AddメソッドでmyRandomInfoに追加していきます。
最終的には30までの数字しか表示しないので、1から30までの繰り返しでいいのでは、と思われるかもしれませんが、それでは、必ず30の値が常に、最後に表示されてしまいます。1から29まではランダムに並びますが、30は常に最後に表示されます。そこで、31までの乱数を発生させて、その中から30までのランダムな数字を表示させています。
GridViewのItemsSourceプロパティにmyRandomInfoオブジェクトを指定します。これで、1から30までのランダムな数字が表示されます。

Private Sub DataShow()
  myCount = 0
  Rnd = New Random
  Dim myRnd As Integer = 0
  Dim temp As Integer = 0
  Dim myArray(31) As Integer
  Dim myRandomInfo As New List(Of RandomInfo)
  For i As Integer = 1 To 31
    myArray(i) = i
  Next
 
  For i As Integer = 1 To 31
    myRnd = Rnd.Next(i, 31)
    temp = myArray(myRnd)
    myArray(myRnd) = myArray(i)
    myArray(i) = temp
    If i < 31 Then
      myRandomInfo.Add(New RandomInfo With {.乱数 = myArray(i)})
    End If
  Next
  GridView1.ItemsSource = myRandomInfo
End Sub

ランダムに並んでいる数字を選択した時の処理

メンバー変数noの値を1ずつ増加させます。変数myNoに、GridView1から選択された項目を、RandamInfoクラスにキャストして、その「乱数」プロパティの値を取得して格納しておきます。
取得した乱数の値が1ずつ増加するメンバー変数noと同じなら、非表示になっているdummyTextBox内にその値を表示させます。そうでない場合は、エラー音を発生させて、1ずつ増加させていたメンバー変数noの値を1ずつ減少させます。
メンバー変数myCountの値が、ComboBoxから選択された値を格納しているメンバー変数mySecondと同じか小さく、且つ、「乱数」プロパティの値が「30」で、且つメンバー変数noの値が「30」なら、クリアしたことになるので、EndMessageメソッドを実行します。メンバー変数_secondの値を0で初期化します。_secondメンバー変数にはComboBoxから選択された秒数が格納されているメンバー変数です。

  Private Sub GridView1_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles GridView1.SelectionChanged
    Try
      no += 1
      flagNo = no
      Dim myNo = DirectCast(GridView1.SelectedItem, RandomInfo).乱数
      If myNo = no Then
        dummyTextBox.Text = flagNo
      Else
        MediaElement1.Play()
        no -= 1
      End If

      If myCount <= mySecond AndAlso DirectCast(GridView1.SelectedItem, RandomInfo).乱数 = "30" AndAlso no = 30 Then
        EndMessage()
        _second = 0
      End If
    Catch
      Exit Sub
    End Try
  End Sub

[開始]ボタンがタップされた時の処理

メッセージを表示させるmessageTextBlockを非表示にします。ComboBoxからの秒数の選択を不可とします。
ランダムな数値を表示させるDataShowメソッドを実行します。[開始]ボタンの使用を不可とします。
新しいDispatcherTimerクラスのインスタンスmyTimerオブジェクトを作成します。Intervalに「1秒」を指定します。
AddHandlerステートメントで、指定したタイマーの間隔が経過し、タイマーが有効である場合に発生するTickイベントに、myTimer_Tickイベントハンドラを指定します。Startメソッドでタイマーを開始します。

  Private Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
    messageTextBlock.Visibility = Windows.UI.Xaml.Visibility.Collapsed
    ComboBox1.IsEnabled = False
    DataShow()
    StartButton.IsEnabled = False
    myTimer = New DispatcherTimer
    myTimer.Interval = New TimeSpan(0, 0, 1)
    AddHandler myTimer.Tick, AddressOf myTimer_Tick
    myTimer.Start()
  End Sub

指定したタイマーの間隔が経過し、タイマーが有効である場合に発生するTickイベント処理

指定した秒数内でクリアできた場合はEndMessageメソッドを実行します。指定した秒数内にクリアできなかった場合は、FalseMessageメソッドを実行します。このFalseMessageメソッド処理は省略しています。ダウンロードしたサンプルのソースコードを参照してください。
それ以外の場合は、秒数を1ずつ減算し、メンバー変数myCountを1ずつ増加させます。timeTextBlockに秒数がマイナスされながら表示されます。

  Private Sub myTimer_Tick()
    If myCount <= mySecond AndAlso no.Equals(dummyTextBox.Text) Then
      EndMessage()
      _second = 0
      timeTextBlock.Text = "残り" & _second & "秒"
      Exit Sub
    ElseIf myCount > mySecond AndAlso Not no.Equals(dummyTextBox.Text) Then
      _second = 0
      timeTextBlock.Text = "残り" & _second & "秒"
      FalseMessage()
      Exit Sub
    Else
      timeTextBlock.Text = "残り" & _second & "秒"
      _second -= 1
      myCount += 1
    End If

  End Sub

クリアできた時の処理

タイマーを停止。ランダムに表示されていた数字をクリアします。
キャラクターに喋らせる言葉を格納するメンバー変数readingTextに値を指定しています。
syokoVoiceタスクを実行。messageTextBlockに「おめでとうございます!」と表示し、非表示から表示状態に切り替えます。
メンバー変数noを0で初期化します。秒数が格納されているメンバー変数_secondを0で初期化します。
timeTextBlockに残り時間を表示し、選択された数値を表示させていたdummyTextBox(非表示)内を空にします。ComboBox1の選択を可能にし、ComboBox1で選択されていた項目を解除します。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

  Private Async Sub EndMessage()
    myTimer.Stop()
    GridView1.ItemsSource = Nothing
    'GridView1.IsEnabled = False
    readingText = "おめでとうございます。時間内にクリアできました。"
    Await syokoVoice()
    messageTextBlock.Text = "おめでとうございます!"
    messageTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible
    no = 0
    _second = 0
    timeTextBlock.Text = "残り" & _second & "秒"
    dummyTextBox.Text = String.Empty
    ComboBox1.IsEnabled = True
    ComboBox1.SelectedIndex = -1
    'RemoveHandler myTimer.Tick, AddressOf myTimer_Tick
  End Sub

結果をキャラクターが音声で喋る処理

この処理は、ここでは省略しているEndMessageやFalseMessageメソッドから呼び出しています。詳しくはダウンロードしたサンプルのソースコードを参照してください。
MediaElement型のmyMedia変数を宣言し、syokoMediaElementで初期化しておきます。
音声機能へのアクセスを提供する、新しいSpeechSynthesizerのインスタンス、synthオブジェクトを作成します。
SynthesizeTextToStreamAsyncメソッドで、指定した文字列から、音声出力を非同期に生成します。
SetSourceメソッドで、指定されたストリームおよびMIME型を使用してSourceプロパティを設定します。Playメソッドで音声を再生します。
音声にどんな言語で、どのような声で喋らすかは、SpeechSynthesizerのVoiceプロパティで参照できます。下記のURLを参照してください。
> SpeechSynthesizer.Voice | voice property

上記ページによると、Japanese JA は性別が「Female」で、名前は「Haruka」という女性が読み上げるようです。
非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

Private Async Function syokoVoice() As Task
  Dim myMedia As MediaElement = Me.syokoMediaElement
  Dim synth = New Windows.Media.SpeechSynthesis.SpeechSynthesizer
  Dim stream = Await synth.SynthesizeTextToStreamAsync(readingText)
  myMedia.SetSource(stream, stream.ContentType)
  myMedia.Play()
End Function

今回はここまでです。次回の記事をお楽しみに。

  • キャラクターが音声で応援する脳トレーニングアプリのサンプルプログラム

    『Windows 8.1+Visual Studio 2013によるWindows ストア・アプリ開発実例集』 第1回のサンプルプログラムです。
薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています