この記事は Microsoft Power BI Advent Calendar 2024 の12日目の記事です。
はじめに
Power Apps での知育アプリ作りから派生して、Power BI でも知育アプリを作ってみました。今年の 1月に「★の色を変えるアプリ」を Power BI で作りましたが、今回はゲーム性(ランダム性や正誤判定) があるアプリを作りたいと思います。
作るもの
足し算の和を答える「けいさんクイズ」ゲームを作ります。
実際の動作画面
なお、先行して Power Apps でも同様のアプリを作成済み。今回は、このアプリを Power BI で再現するような形です。
必要な要素
- ランダム性
- 設問の切り替え
- 解答入力欄
- 正誤判定
- ヒント機能
諦めたこと
- 正解数の蓄積
- サウンド
- アニメーション
※GIF アニメなら導入できるようです。
データ作成
Power Query で、設問のベースになるテーブルを作成します。
レベルとインデックス
レベルを 3段階に分けて、各レベル 20問用意することにします。
このリストの作成には、List.Numbers
関数 を利用しました。
レベルを表すリストは、1 が 20回、2 が 20回、3 が 20回繰り返されます。要素数60のリスト・増分を 1/20(=0.05) として、あとで小数点以下を切り捨てることで実現しています。
List.Numbers(1,60,1/20)
各レベルごとの番号 (インデックス) は、1~20 の連番が 3回繰り返されるリストです。List.Numbers(1,20)
を List.Repeat で 3回繰り返す形で実現しました。
List.Repeat(List.Numbers(1,20),3)
Table.FromColumns
関数で、この 2つのリストをテーブル化します。
= Table.FromColumns({List.Numbers(1,60,1/20),List.Repeat(List.Numbers(1,20),3)}, {"Level","Index"})
レベルとインデックスを記号化
レベルを★表記に、インデックスを ABC 表記にした列を作成しておきます。これらを数値のままアプリで使うと、設問・解答の数値と合わせて画面上が数値だらけになってわかりにくかったため。
Text.Repeat("★", [Level])
Character.FromNumber(64 + [Index])
({"A".."T"}
でもいけたっぽい)
足し算する数値の列
つづいて、設問の足し算に使う数値の列を作成します。
繰り上がりの計算はちょっと難しくなるので、レベル1 では設問の答えが最大でも 10 になるように、足す数の最大値を 5 に設定します。レベル2 では繰り上がりがあったりなかったり(3~10)、レベル3 では確実に繰り上がりが発生する(5~15) ように設計しました。
なお、このあとの Number.RandomBetween
関数の処理で小数点以下を切り捨てる前提で、式における最大値は [設定したい値 + 1] になっています。
最小値 MinRand:each if [Level] = 1 then 1 else if [Level] = 2 then 3 else if [Level] = 3 then 5 else null)
最大値 MaxRand:each if [Level] = 1 then 6 else if [Level] = 2 then 11 else if [Level] = 3 then 16 else null)
MinRand ~ MaxRand の範囲で、ランダムな数値列を 2つ作成します。最小値と最大値の間でランダムな値を返す Number.RandomBetween
関数を利用しました。
Rand1:each Number.RoundDown(Number.RandomBetween([MinRand], [MaxRand]), 0))
Number.RandomBetween
関数で返されるのは、整数ではなく小数です。Number.RoundDown
関数で小数点以下を切り捨てることで値を整数にしています。範囲内の整数を返す Excel の RANDBETWEEN
関数とは挙動が異なるんですね。
ランダムな数値の和
ランダムに作った数値の 2列を足し算します。こういった単純な計算の場合は、対象の 2列を選択した状態で 列の追加 > 標準 > 加算 で作成できます。こういうときになぜかカスタム列の追加から手入力しちゃってたので、GUI も有効活用しないとです。
解答の仕様の都合で、十の位と一の位の列を分けて持つ必要があります。ここもいくつかの方法が考えられそうですが、下記のとおり実装。
十の位 AnswerSliced10:each Number.IntegerDivide([AnswerAdd], 10)
※列の追加 > 標準 > 除算 (整数)
一の位 AnswerSliced1:each Number.Mod([AnswerAdd], 10)
※列の追加 > 標準 > 剰余
正解時に表示されるメッセージ
正解時に表示されるメッセージは、複数パターンを用意しました。先ほどのように Number.RandomBetween
関数を使ってランダムにしてもいいんですが、ここでは Number.Mod
関数を使ってみました。正解の値を 4で割った時の余り(0~3)によって、4種類の異なるメッセージが表示されます。
Correct:
each let mod4 = Number.Mod([AnswerAdd], 4) in if mod4 = 0 then "せいかい!" else if mod4 = 1 then "やったね!" else if mod4 = 2 then "いえーい!" else if mod4 = 3 then "はなまる!" else "")
let
を使って「正解の値を 4で割った時の余り」の結果に名前をつける形で、式を簡略化しました。Excel の LET
関数や LAMBDA
関数、Power Apps の With
関数と同様、一時的な変数を作れるような機能です。
メインのテーブル完成
ETL ツールでひたすらデータを「作る」という謎作業を終えて、こんな感じのテーブルが完成しました。
解答選択用のテーブル作成
解答するためのスライサーに用いるため、十の位・一の位それぞれについて 0~9 の値を持ったテーブルを作成します。選択肢のマスタのようなものです。中身は全く同じですが、十の位・一の位で別のスライサーとして機能する必要があるので、十の位・一の位それぞれ作成しています。
十の位解答用 Select10:Table.FromColumns({List.Numbers(0,10)}, {"Select10"})
一の位解答用 Select1:Table.FromColumns({List.Numbers(0,10)}, {"Select1"})
リレーション作成
メインのテーブルの AnswerSliced
列とリレーションを張ります。スタースキーマっぽくなってる。
設問選択
レベル・設問スライサー
表示対象のレコードを 1つに絞るために、レベルとインデックスのスライサーを配置します。いずれも選択必須・単一選択です。右に置いた確認用のテーブルで、レコードが絞られていることを確認。
設問の数値表示
カードで Rand1・Rand2 を表示しているだけです。+と=はテキストボックスで。
(ボツ案:ブックマークで設問選択)
ひとつの設問だけを表示するブックマークを作って、画面移動を実現しようとしましたが、断念しました。ブックマークを 20個作るのがしんどかったので。
解答選択
解答選択スライサー
解答選択用テーブルに作った Select10・Select1 列を使って、スライサーを 2つ配置します。設問選択と同じく、選択必須・単一選択です。
2×5 になるようにサイズを調整。文字がこれ以上大きくできなかったり、選択時の色が指定できなかったり、普段はあまりこだわらないところですが、見た目の制約が意外と多いことに気づきますね。
選択した数字の表示
選択した 2つの数字が 2ケタの数値に見えるように、カードを配置します。この数値は常に表示されるものなので、マスタ側である Select10・Select1 を表示しています。
十の位については、0 が選択されているときは何も表示されないようにしています。
吹き出しの値のカラーの設定で、0 (か空白) のときは背景色と同じ色にすることで、表示されていないように見せています。
正解判定メッセージ
解答選択スライサーで選択した数字が、レコードの十の位・一の位とそれぞれ一致していれば、そのレコードの正解メッセージを表示します。
スライサーの選択が間違っている場合、返されるレコードがないため、(空白)が表示されてしまいます。先ほどと同様、吹き出しの値のカラー設定で、空白の場合は背景色と同じ色に設定することで、非表示に見せています。
要改善:設問変更時に、解答がリセットされない
表題のとおりのため、現在選択されている解答=変更後の設問の答え になっている場合、設問変更した時点で正解メッセージが表示されてしまいます。イケてない仕様ですが、解消方法が思いつかず…
(ボツ案:Text Filterで解答)
当初、Text Filter で解答する形にしようとしていましたが、断念しました。
Text Filterは完全一致ではなく部分一致なので、たとえば 1 を入力すると、設問の答えが 10 でも 15 でも 21 でも正解扱いになってしまいます。
※Text Filter で扱えるように、数値型ではなくテキスト型にしています
完全一致に変更するオプションもなさそうだったので、数値入力の UI を作った次第です。
ビジュアル
ヘッダーアイコン
すべてオフにします。好奇心旺盛な 3~5歳程度の子どもを想定ユーザーにしているので、余計な要素は表示させないようにしています。
相互作用
スライサー操作時の相互作用を正しく設定する必要があります。ここが一番の学びになったところかも。
設問選択スライサーは全体の 60問から 1問をフィルターする役割なので、他のほとんどのビジュアルをフィルターします。
解答選択スライサーはその 1問(の正解メッセージ)を表示するかしないかを決める役割です。そのため、フィルターする範囲は限定的。例えば、解答選択スライサー → 足す数の 1つ目 の相互作用を無効にしないと、フィルターされて該当レコードがない(空白) と表示されることになります。
また、スライサーで選択できる項目はすべて固定で、他のスライサーの選択に影響を受けません。
まとめると、下記のとおり。
- 設問選択スライサーは、他のスライサー以外をフィルターする
- 解答選択スライサーは、解答と正解メッセージのみフィルターする
- スライサー同士は、お互いをフィルターしない
ビジュアルを追加したときは、上記のルールに従って各スライサーからの相互作用を編集する必要があります。既存ビジュアルをコピーする方が楽ではありますが、思わぬところで不要な設定が残るリスクもあるので、一長一短なのかなと感じました。
ヒント表示
足す数と同じ分の丸を表示させ、丸を数えることで計算の助けにしようという意図です。
必要に応じて表示・非表示を切り替えられるように、ブックマーク機能を利用します。ヒントを表示した状態のブックマークと、ヒントが非表示のブックマークを作成し、ボタンで遷移できるようにします。
↑↓
また、遷移時に設問が変更されないように、ブックマークの「データ」のチェックは外しておきます。
モバイルレイアウト
スマホで閲覧できるように、モバイルレイアウトを作成します。レイアウトセンスは修業が足りん… ブックマーク機能でレベル・設問選択を常時表示させない形も検討しましたが、ユーザーの操作数を減らすことを優先して常時表示に戻しました。
長女(4歳)のテストプレイより
表示速度は問題ない
集中力が途切れるようなロード時間は発生していないようでした。
ヒント小さすぎ
ヒント表示ボタンは直感で理解してもらえたものの、表示される丸が小さすぎて数え間違えが起きていました。サイズをもっと大きくしたり、5・10 単位で区切ったり、といった工夫が必要そうです。
ビジュアルのダブルタップで拡大される
Power BI モバイルアプリの標準機能で、ビジュアルをダブルタップするとフォーカスモードになるようです。そのビジュアルだけが拡大表示され、元の画面に戻るには、左上の矢印ボタンか、端末の戻るボタンを押す必要があります。
さすがにこの機能をオフにすることはできなさそうなので、「変な画面になったら左上の矢印を押して」と長女に伝えて、運用でカバーすることにしました。
下方向への謎スクロール
スマホの画面内に収まるように余裕を持ってレイアウトしたはずなんですが、下方向にちょっとだけスクロールする余地あります。レベル選択ボタンの上半分が隠れるくらい。こういう余地を見つけると、すぐ子どもは上下に高速小刻みスクロールして遊んじゃうんですよね… 大きな影響はないものの、何が原因でスクロール余地ができているのかが気になります。
おわりに
いろいろと諦めた点はあったものの、とりあえず遊べるクイズアプリが作れました。やってやれないことはない。
今まであまり使っていなかった機能を触るきっかけになりました。相互作用の編集を細かくいじってみたり、モバイルレイアウトを作成してスマホで開いてみたりですね。
Power Query でゼロからデータを作成する、というあまり使わなさそうな技術も学べました。外部データソースに頼らずにデータが作れるようになれば、ちょっとした検証用途には役立つかも。
Power Query でのデータ作成では、やりたいことを実現する方法はひとつではないことを実感できます。パフォーマンスを追求するなら、複数のやり方を考えて比較する必要も出てきますね。
一方で、データ作成の部分は、メジャーでやるべきところもあったのかも、と思っています。ISSELECTEDMEASURE
とか SELECTEDVALUE
とか HASONEFILTER
とか、DAX でも担えそうな関数はあるので。
知育アプリはおとなしく Power Apps で作るとして、Power BI はデータ可視化・分析のツールとして使うべきですね(何をいまさら)。「時間の家計簿」に新しい項目(体重・ながらバイクの累積距離など)を追加しているので、それらを反映したアップデートもしたいなと。