12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Power BI で知育アプリを作る ~けいさんクイズ編~

Last updated at Posted at 2024-12-11

この記事は 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"})

image.png

レベルとインデックスを記号化

レベルを★表記に、インデックスを ABC 表記にした列を作成しておきます。これらを数値のままアプリで使うと、設問・解答の数値と合わせて画面上が数値だらけになってわかりにくかったため。

Text.Repeat("★", [Level])

Character.FromNumber(64 + [Index])

{"A".."T"} でもいけたっぽい)

image.png

足し算する数値の列

つづいて、設問の足し算に使う数値の列を作成します。

繰り上がりの計算はちょっと難しくなるので、レベル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 も有効活用しないとです。

image.png

解答の仕様の都合で、十の位と一の位の列を分けて持つ必要があります。ここもいくつかの方法が考えられそうですが、下記のとおり実装。

十の位 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 ツールでひたすらデータを「作る」という謎作業を終えて、こんな感じのテーブルが完成しました。

image.png

解答選択用のテーブル作成

解答するためのスライサーに用いるため、十の位・一の位それぞれについて 0~9 の値を持ったテーブルを作成します。選択肢のマスタのようなものです。中身は全く同じですが、十の位・一の位で別のスライサーとして機能する必要があるので、十の位・一の位それぞれ作成しています。

十の位解答用 Select10:Table.FromColumns({List.Numbers(0,10)}, {"Select10"})

一の位解答用 Select1:Table.FromColumns({List.Numbers(0,10)}, {"Select1"})

リレーション作成

メインのテーブルの AnswerSliced列とリレーションを張ります。スタースキーマっぽくなってる。

image.png

設問選択

レベル・設問スライサー

表示対象のレコードを 1つに絞るために、レベルとインデックスのスライサーを配置します。いずれも選択必須・単一選択です。右に置いた確認用のテーブルで、レコードが絞られていることを確認。

image.png

image.png

設問の数値表示

カードで Rand1・Rand2 を表示しているだけです。+と=はテキストボックスで。

image.png

(ボツ案:ブックマークで設問選択)

ひとつの設問だけを表示するブックマークを作って、画面移動を実現しようとしましたが、断念しました。ブックマークを 20個作るのがしんどかったので。

解答選択

解答選択スライサー

解答選択用テーブルに作った Select10・Select1 列を使って、スライサーを 2つ配置します。設問選択と同じく、選択必須・単一選択です。

2×5 になるようにサイズを調整。文字がこれ以上大きくできなかったり、選択時の色が指定できなかったり、普段はあまりこだわらないところですが、見た目の制約が意外と多いことに気づきますね。

image.png

選択した数字の表示

選択した 2つの数字が 2ケタの数値に見えるように、カードを配置します。この数値は常に表示されるものなので、マスタ側である Select10・Select1 を表示しています。

image.png

十の位については、0 が選択されているときは何も表示されないようにしています。

image.png

吹き出しの値のカラーの設定で、0 (か空白) のときは背景色と同じ色にすることで、表示されていないように見せています。

image.png

正解判定メッセージ

解答選択スライサーで選択した数字が、レコードの十の位・一の位とそれぞれ一致していれば、そのレコードの正解メッセージを表示します。

image.png

スライサーの選択が間違っている場合、返されるレコードがないため、(空白)が表示されてしまいます。先ほどと同様、吹き出しの値のカラー設定で、空白の場合は背景色と同じ色に設定することで、非表示に見せています。

image.png

image.png

要改善:設問変更時に、解答がリセットされない

表題のとおりのため、現在選択されている解答=変更後の設問の答え になっている場合、設問変更した時点で正解メッセージが表示されてしまいます。イケてない仕様ですが、解消方法が思いつかず…

(ボツ案:Text Filterで解答)

当初、Text Filter で解答する形にしようとしていましたが、断念しました。

Text Filterは完全一致ではなく部分一致なので、たとえば 1 を入力すると、設問の答えが 10 でも 15 でも 21 でも正解扱いになってしまいます。

image.png

※Text Filter で扱えるように、数値型ではなくテキスト型にしています

完全一致に変更するオプションもなさそうだったので、数値入力の UI を作った次第です。

ビジュアル

ヘッダーアイコン

すべてオフにします。好奇心旺盛な 3~5歳程度の子どもを想定ユーザーにしているので、余計な要素は表示させないようにしています。

相互作用

スライサー操作時の相互作用を正しく設定する必要があります。ここが一番の学びになったところかも。

設問選択スライサーは全体の 60問から 1問をフィルターする役割なので、他のほとんどのビジュアルをフィルターします。

解答選択スライサーはその 1問(の正解メッセージ)を表示するかしないかを決める役割です。そのため、フィルターする範囲は限定的。例えば、解答選択スライサー → 足す数の 1つ目 の相互作用を無効にしないと、フィルターされて該当レコードがない(空白) と表示されることになります。

image.png

また、スライサーで選択できる項目はすべて固定で、他のスライサーの選択に影響を受けません。

まとめると、下記のとおり。

  • 設問選択スライサーは、他のスライサー以外をフィルターする
  • 解答選択スライサーは、解答と正解メッセージのみフィルターする
  • スライサー同士は、お互いをフィルターしない

ビジュアルを追加したときは、上記のルールに従って各スライサーからの相互作用を編集する必要があります。既存ビジュアルをコピーする方が楽ではありますが、思わぬところで不要な設定が残るリスクもあるので、一長一短なのかなと感じました。

ヒント表示

足す数と同じ分の丸を表示させ、丸を数えることで計算の助けにしようという意図です。

必要に応じて表示・非表示を切り替えられるように、ブックマーク機能を利用します。ヒントを表示した状態のブックマークと、ヒントが非表示のブックマークを作成し、ボタンで遷移できるようにします。

image.png

↑↓

image.png

また、遷移時に設問が変更されないように、ブックマークの「データ」のチェックは外しておきます

image.png

モバイルレイアウト

スマホで閲覧できるように、モバイルレイアウトを作成します。レイアウトセンスは修業が足りん… ブックマーク機能でレベル・設問選択を常時表示させない形も検討しましたが、ユーザーの操作数を減らすことを優先して常時表示に戻しました。

image.png

長女(4歳)のテストプレイより

表示速度は問題ない

集中力が途切れるようなロード時間は発生していないようでした。

ヒント小さすぎ

ヒント表示ボタンは直感で理解してもらえたものの、表示される丸が小さすぎて数え間違えが起きていました。サイズをもっと大きくしたり、5・10 単位で区切ったり、といった工夫が必要そうです。

ビジュアルのダブルタップで拡大される

Power BI モバイルアプリの標準機能で、ビジュアルをダブルタップするとフォーカスモードになるようです。そのビジュアルだけが拡大表示され、元の画面に戻るには、左上の矢印ボタンか、端末の戻るボタンを押す必要があります。

Screenshot_2024-12-11-04-06-13-79_5957ebf05589ce547f0347b86fd73533.jpg

さすがにこの機能をオフにすることはできなさそうなので、「変な画面になったら左上の矢印を押して」と長女に伝えて、運用でカバーすることにしました。

下方向への謎スクロール

スマホの画面内に収まるように余裕を持ってレイアウトしたはずなんですが、下方向にちょっとだけスクロールする余地あります。レベル選択ボタンの上半分が隠れるくらい。こういう余地を見つけると、すぐ子どもは上下に高速小刻みスクロールして遊んじゃうんですよね… 大きな影響はないものの、何が原因でスクロール余地ができているのかが気になります。

おわりに

いろいろと諦めた点はあったものの、とりあえず遊べるクイズアプリが作れました。やってやれないことはない。

今まであまり使っていなかった機能を触るきっかけになりました。相互作用の編集を細かくいじってみたり、モバイルレイアウトを作成してスマホで開いてみたりですね。

Power Query でゼロからデータを作成する、というあまり使わなさそうな技術も学べました。外部データソースに頼らずにデータが作れるようになれば、ちょっとした検証用途には役立つかも。

Power Query でのデータ作成では、やりたいことを実現する方法はひとつではないことを実感できます。パフォーマンスを追求するなら、複数のやり方を考えて比較する必要も出てきますね。

一方で、データ作成の部分は、メジャーでやるべきところもあったのかも、と思っています。ISSELECTEDMEASURE とか SELECTEDVALUE とか HASONEFILTER とか、DAX でも担えそうな関数はあるので。

知育アプリはおとなしく Power Apps で作るとして、Power BI はデータ可視化・分析のツールとして使うべきですね(何をいまさら)。「時間の家計簿」に新しい項目(体重・ながらバイクの累積距離など)を追加しているので、それらを反映したアップデートもしたいなと。

12
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?