キャンバスアプリ 脳トレ漢字の間違い探しアプリの作り方 その② 拡張版

コンテンツ

はじめに

この記事は、Microsoft Power Apps Advent Calendar 2024 12月12日担当分の記事です。

先日、夜思い付いて脳トレでよくある漢字の間違い探しのアプリをつくってみました。
最初に1時間くらいで作って基本機能は出来ました。そこから、次の日くらいに思いついた拡張機能を追加していった感じです。

その①の記事でベータ版の作成を記載しました。その②では拡張機能を追加していきます。
拡張の要素も結構あるので、最初の3つと微調整までが本記事の内容となります。(続きはその③で)

サンプル動画

YouTubeに①ベータ版と②拡張版の動画をアップしました。


漢字間違いを探すアプリの機能について

その①ベータ版では10問固定で、表示個数も固定、連続出題、60秒経つか全問正解で終了画面へ。という内容でした。

ここに以下の拡張機能を追加していきます。★を付けているのが本記事の対応内容です。

拡張要件
  • ランダムに出題されるようにする ★
  • 表示する文字数を変更して難易度を選択できる ★
  • 10問、20問、30問とタイムアタックモードを選択できる ★
  • ユーザー好みのカラーを選択できる (なぜかその①で入れちゃったけど本来後回しでよい)
  • ランキング一覧を追加する
  • CLEAR履歴や選択カラーをキャッシュで保持する
  • 終了ボタン、現在の難易度モードを表示する ★

①ベータ版をベースに追加していきます。そちらの作り方や概要は前回記事をご参考ください。

ランダムに出題されるようにする

前回は10問固定で書いていました。ただそれだとつまらないのでランダムに出題されるように変更します。また、30問モードなどもあるので100個くらいのセットを用意したいと思います。
ただ、純粋に用意していくのも手間ですよね。そういった時こそAI(Copilot)の出番ですね。

Copilot in Edge で問題を作ってもらう

Edgeの右上にあるやつで以下のように書いて作ってもらいました。
Copilot in Edge | Microsoft Learn

前回はIDを付けてましたが、ランダムに出題する際に別途追加するので、idはなくして正、誤の組み合わせてで作ってももらいます。

上記のように作ってくれるのでコピーして貼り付けます。
ただ、やはり見た目が似ている漢字と伝えても全然違うやつがあったり、意味合いが似てる?的なものがあったり、重複が有ったりします。なのでチェックしつつイケるやつを追加して、何度かプロンプトを繰り返したりするといいかと思います。

App.Formulasに問題セットを定義する

以下のように漢字のセットを追加します。
前回はIDもありましたが別で付けるので無くしてます。
また、KanjiColとしてましたが、名前をKanjiDefに変えています。(KanjiColはランダム調整時に別途作成)

CodekanjiDef=[
  {trueText:”右”, falseText:”左”},
・・・・・・
  {trueText:”感”, falseText:”惑”}
];
→100問位のセットを用意する

シャッフルしてコレクションに格納してランダム出題にする

スタート画面の開始ボタンを以下のように修正します。
追加する処理としては定義(KanjiDef)をShuffle関数でシャッフルしてFirstN関数で最初の10問を取得します。そこにAddColumns関数でid列をとりあえず0を設定して追加しています。
それをForAllで件数分Sequence関数でループしてIndex関数でid列に順番に番号を付けています。

CodeClearCollect(kanjiCol,AddColumns(FirstN(Shuffle(kanjiDef),10),id,0));
ForAll(Sequence(CountRows(kanjiCol)) As i,
    Patch(kanjiCol,Index(kanjiCol,i.Value),{id:i.Value});
);

TIPSIndex関数でインデックスを付ける処理は以下も参考ください
Index関数でスマートに(エラー対応考慮も)&インデックス列の追加サンプル | Power Apps Tips ログ

ランダムに出題されていることを確認

そうすると以下のようにコレクションがランダムで作成され、IDが1~10まで付与されています。
開始ボタンをクリックして以下のようにランダムに問題が出ていればOKです。何度かやってみて毎回別の問題となる事を確認しましょう。

難易度を追加する(ギャラリーのマス目を動的に変更)

次の機能では難易度として、ギャラリーに表示する件数を3段階に調整します。

ギャラリーの折り返しが現在「9」で固定となっているので、変数にしてスタート画面で設定するようにします。先にスタート画面側の処理を記載します。

スタート画面に難易度設定のボタンを用意する

まず変数「galsize」としているのを5,7,9の3段階で設定します。(数値は任意)
サンプルでは以下のようにボタンをデザインしました。
易、普、難の文字にして色も緑、青、赤にしてそれぞれOnSelectで Set関数で5、7、9と設定するようにします。

デザイン的に選択時にはメインカラー、選択されていない場合は白にして文字色に色を反映などしてます。このあたりはお好みの範囲なので調整してください。以下に上記ボタンのコードを貼っておきます。

易しいボタン
ButtonCanvas_Eazy:
Control: Button
Properties:
OnSelect: |
=Set(galsize,5)
Appearance: =If(galsize=5,"Primary","Secondary")
BasePaletteColor: =RGBA(8, 222, 8, 1)
BorderColor: =If(galsize=5, App.Theme.Colors.Primary)
BorderRadiusBottomLeft: =25
BorderRadiusTopLeft: =25
BorderStyle: =BorderStyle.Solid
BorderThickness: =10
Font: =Font.Verdana
FontColor: =If(galsize<>5,RGBA(156, 220, 79, 1),Color.White)
FontSize: =40
Icon: =
Layout: ='ButtonCanvas.Layout'.TextOnly
Text: ="易"
Height: =121
Width: =195
X: =92
Y: =206
普通ボタン
- ButtonCanvas_Normal:
    Control: Button
    Properties:
      OnSelect: =Set(galsize,7)
      Appearance: =If(galsize=7,"Primary","Secondary")
      BorderColor: =If(galsize=7, App.Theme.Colors.Primary)
      BorderStyle: =BorderStyle.Solid
      BorderThickness: =10
      Font: =Font.Verdana
      FontColor: =If(galsize<>7,RGBA(5, 102, 178, 1),Color.White)
      FontSize: =40
      Icon: =
      Layout: ='ButtonCanvas.Layout'.TextOnly
      Text: ="普"
      Height: =121
      Width: =195
      X: =287
      Y: =206
難しいボタン
- ButtonCanvas_Hard:
    Control: Button
    Properties:
      OnSelect: =Set(galsize,9)
      Appearance: =If(galsize=9,"Primary","Secondary")
      BasePaletteColor: =RGBA(249, 83, 109, 1)
      BorderColor: =If(galsize=9, App.Theme.Colors.Primary)
      BorderRadiusBottomLeft: =0
      BorderRadiusBottomRight: =25
      BorderRadiusTopLeft: =0
      BorderRadiusTopRight: =25
      BorderStyle: =BorderStyle.Solid
      BorderThickness: =10
      Font: =Font.Verdana
      FontColor: =If(galsize<>9,Color.DarkRed,Color.White)
      FontSize: =40
      Icon: =
      Layout: ='ButtonCanvas.Layout'.TextOnly
      Text: ="難"
      Height: =121
      Width: =195
      X: =482
      Y: =206

TIPS重要なのはSet(galsize,〇)の部分なので、そこだけあってればデザインは気にせずかまいません。
自分はなんとなくイメージした切替えボタンを作ったという感じです。

ギャラリーの折り返しとサイズを動的に切り替える

次にメイン画面のギャラリーの設定を調整します。

ギャラリーの折り返しが現在「9」で固定としていますが、変数:galsizeにして5,7,9の3段階にします。
また、表示個数は9の場合は9×9で81のマス目でしたが、5の場合は25、7の場合は49とします。

変更前の状態

変更内容

上記から以下を設定します。折り返しの数に合わせてサイズも調整されるようにテンプレートのサイズと中のボタンのサイズとフォントサイズを調整しています。

  • 折り返しの数:galsize
  • テンプレートのサイズ: Self.Width / galsize
  • テンプレートのパディング:3
  • 中のボタンの幅: Parent.TemplateWidth
  • 中のボタンの高さ:Parent.TemplateHeight
  • 中のボタンのフォントサイズ: Self.Width/1.6

上記のように動的に表示個数とボタンのサイズ、文字サイズなど変更されればOKです。
ギャラリーの高さが足りない場合や大きすぎる場合はちょうどよいサイズに調整して下さい。

ギャラリーのマス目数を動的に調整する

上記でギャラリーの折り返し数や表示は調整しましたが、Itemsに指定している:galcol (マス目数)は固定で81になってます。

こちらをギャラリーの折り返し数に合わせて調整します。以前はApp.Formulasに定義していましたが、動的に変更するのでこちらからは削除します。

一旦エラーとなりますが、スタート画面に移動して「開始」ボタンのOnSelectに以下を追加します。

CodeSet(galCol,Sequence(galsize * galsize,1));

上記でgalsize ×galsizeのマス目になるので、5→25、7→49、9→81 になりますね。エラーが解消されると思います。

開始ボタンをクリックしてそれぞれ動的に変更されているか確認しましょう。

App.OnStartで初期値を設定

最初に起動した際の初期値を入れておきます。App.OnStartに以下を追加します。

CodeSet(galsize,5);
※易しいを指定してますが、普通の7でもいいです。

10問、20問、30問とタイムアタックモードを追加する

つぎに現状は開始ボタンで10問固定、60秒制限で終了の1つのモードですが、10問、20問、30問、タイムアタック(60秒)の4つのモードを用意して切り替えるようにします。

10~30問のモードは最大の制限時間は300秒としておいて、それまでに何秒でクリアできるか。というモード、タイムアタックは従来どおり60秒にして何問回答出来るか。というモードになります。

スタート画面のボタンを以下のイメージに変更してそれぞれのボタンでモード選択&ゲーム開始とするイメージです。

4つのボタンを追加

詳細は割愛しますが、10問、20問、30問、タイムアタックとしたボタンを4つ追加します。
それぞれのOnSelectには 問題数用の変数:Max をそれぞれ10,20、30、0(タイムアタック)で指定して、従来の開始ボタンをSelectして呼び出すようにします。

TIPSSelect(ボタン名)で疑似的にボタンをクリック出来るので、上記のように同じ処理を実施する場合は共通化出来ます。便利なテクニックですので、使える場合は使いましょう。

開始ボタンを調整(サイズ0で見えなくする)

元々の開始ボタンは幅や高さを0にし(見えないようにして)隅っこに移動しておきます。

現在のデフォルト設定だと非表示のコントロールは読み込みしてくれないので処理が実行されません。そのため上記のように見えないようにサイズや色を調整して非表示にはしない。とします。

NOTE非表示でも動作するように設定で変更も可能ですが、現状は廃止予定の機能に入っているので、どうしてもの場合以外はやめた方がベターです

以前は非表示でよかったが、現状は表示されないと動作しないのでサイズ0などで見えなくする

開始ボタンを調整(問題数を変数に調整)

次に開始ボタンのOnSelectの処理を調整します。Maxの数値に応じて作成する問題数→KanjiColを変更します。
元々10としている部分をMaxに置き換えます。タイムアタックの場合は0になっているのでその場合は100にしておきます。(100まで問題を定義していなくても最大数までになります)

CodeClearCollect(kanjiCol,AddColumns(FirstN(Shuffle(kanjiDef),If(Max=0,100,Max)),id,0));

上記で10、20、30問、タイムアタックは最大100件の問題数がセットされます。

タイマーをモードによって調整する

上記で問題数の調整は出来ましたが、タイマーが60秒で固定になってます。
10~30問モードは300秒、タイムアタックは60秒に調整します。

メイン画面を開いてタイマーのDurationプロパティを以下のように調整します。

正解時の次の問題へボタンを調整

正解時のボタンのOnSelectも調整します。いままではCurrent=10 →10問だったら終了画面へ遷移だったのを選択した問題数の場合にします。

以下のようにMaxに置き換えるだけですね。

TIPSタイムアタックモードの場合は0になっているため、Currentは1始まりなので決して一致しません。そのため制限時間終了後(タイムアタックモードの場合60秒)のナビゲートでのみ終了画面へ遷移するようになってます。※Maxを0にした意味がここにあります

タイマー背景を残り10秒で色変更(任意)

ついでに残り10秒になったら焦らせたいので、色を赤っぽくしたいと思います。ここは完全に任意ですねw
背景を自分の値が10000(10秒)以下になったら赤っぽい色、その他はテーマカラーにしています。

Code//タイマーのFillプロパティ
If(Self.Duration – Self.Value <10000,RGBA(232, 32, 139, 1),App.Theme.Colors.Primary)

モードと難易度の表示、終了ボタンを追加する

今回の最後にスタート画面のヘッダーの位置に終了ボタン、メイン画面に今やっているモードと難易度を表示するラベル(ボタンで作成)を追加しようと思います。また終了画面にもモード、難易度の表示を追加します。

終了ボタンをスタート画面と終了画面に追加

ヘッダーのアイコンは今回全然いらないので非表示にします(モダンヘッダーにした意味はほぼなしですねw)
各画面のヘッダーのプロフィール画像をオフにします。

変わりにその位置にボタンを追加してOnSelectにはExit()としてアプリ終了ボタンを付けます。
スタート画面と終了画面に追加します。サンプルでは以下の感じです。

メイン画面にモードと難易度を表示するボタンを追加

メイン画面に今やっているモードと難易度を表示するボタンを追加します。(ラベルでもいい)
また、スタート画面の色にあわせて難易度に対応するカラーにしてます。

メイン画面もアイコンは削除して以下のようにボタンを配置してTextプロパティとBasePaletteColorプロパティを調整します。

Code// Textプロパティ
Concatenate(If(galsize=5,”やさしい”,galsize=7,”ふつう”,galsize=9,”難しい”),” :”,If(Max=0,”TA”,Max &”問”))
→モードに応じて変更。タイムアタックはTAと表示

//BasePaletteColorプロパティ
If(galsize=5,RGBA(8, 222, 8, 1),galsize=7,RGBA(141, 229, 250, 1),galsize=9,RGBA(249, 83, 109, 1))
→スタートのボタン背景色と同じような色に切り替え ※普通のみちょっと緑がかった青にしてる

以下のようにモードと問題数に応じて内容と色が変わると思います。見た目もいいしユーザーフレンドリーですね。

終了画面にモードと難易度の表示を追加

終了画面を開き、元のやつから以下のように表示を調整しています。
デザインなので詳細は割愛しますが、真ん中のコンテナーにモードと難易度を表示するラベルを追加しています。

Code//モードを表示するラベルのTextプロパティ  ※背景はテーマカラー
If(Max>0, Max & ” 問モード”,”タイムアタックモード”)

//難易度を表示するラベルのTextプロパティ
“難易度:” & If(galsize=9,”難しい”,galsize=7,”普通”,galsize=5,”やさしい”)

おわりに

ということで、拡張機能の3つまで(ランダム出題、難易度調整、問題モード追加)とおまけ(終了ボタン、モード表示調整)まで記事にしました。残りはカラー変更、ランキング機能とその2つをキャッシュして保持する機能となります。こちらはその③で書こうと思いますがおそらく年明け以降となるかと。。
今回までの実装でおおむね主要機能は追加出来たので、残りはおまけのような機能なので対応しなくてもいいんですが。

参考までにカラー変更およびキャッシュして情報を保持するについては以下記事でも書いてますので、そちらを参考にしてもらえれば先に実装出来ると思います。


ランキング機能についても終了画面でコレクションに結果を格納して、並び替え、タブで切替えしているという実装なのでなんとなく作れちゃうかもです。(キャッシュ出来ないとあまり意味はなさそうだが)

TIPSランキングは今回キャッシュ保持にしてますが、SPOリストなどに保持すれば共有したユーザーの成績も残せてみんなでスコアを競うというカスタマイズも可能ですね!

ということで本年もお世話になりました。来年もよろしくお願いいたします。m(__)m 
それではよいお年を!

関連記事


この記事は役に立ちましたか?

もし参考になりましたら、下記のボタンで教えてください。

ヨウセイ

ヨウセイ

一般職からSharePoint、C#、.NET系技術者へ、そこからPower App、Power Automate技術者へと転身。 ワンランク上のおっさんはPower Appsでシステム開発が出来る〜! qiitaや自社HPでも技術ブログを書いていました。

関連記事

コメント

この記事へのコメントはありません。

コンテンツ

CONTENTS