先日から作成していたそろばんアプリが一通り動いたので、工夫、苦労したところと、外観についてまとめておきます。初めてのアプリ作成だったので、かなり色々と苦労しましたが、なんとか動くものができました。手前みそながら、操作性に関して言えば、世に出いているそろばんアプリに引けを取らない出来になったと思っています。
そろばんアプリの共有!
UI
タイトル画面
タイトル画面などなく、問題数、桁数、そろばんサイズを決定して開始する単純なものにしました。設定はスライドバーでシンプルなものにできたと思います。

今回、動かす実施によっては、そろばんが大きかったり、1の位がはみ出したりすることが懸念されました。ですので、そろばんのサイズ調整のバーを付けることで、ある程度柔軟に対応できるように工夫しています。

画面遷移
STARTボタンで問題が開始しますが、画面の遷移の実装について少しふれておきます。開始画面のWindowと問題のWindowを別々に作っておいて、最初使用しない問題のWindowはCanvas GroupコンポーネントのAlphaを0にして透過させておきます。Intractable、Blocks Raycastsもfalseにしておくことで、タッチなどによるフォーカスからも避けることができます。

STARTボタン押下と同時にUpdate()で以下のようにWindow切り替えを行っています。本当はアニメーションでの実装がいいのかもしれませんが、理解するには時間がかかりそうだったので、突貫で作りました。閉じるWindowはalphaの値を段階的に減らして、開くWindowは段階的に増やしています。また、完全に表示されたタイミングでinteractableとblocksRaycastsをtrueにすることで、制御も映しています。
開始画面に戻りたい時はこの逆を実行しています。
void Update()
{
if (changing) {
ProblemWindow.GetComponent<CanvasGroup>().alpha += 0.1f;
ThisWindow.GetComponent<CanvasGroup>().alpha -= 0.1f;
if (ProblemWindow.GetComponent<CanvasGroup>().alpha >= 1) {
ProblemWindow.GetComponent<CanvasGroup>().interactable = true;
ProblemWindow.GetComponent<CanvasGroup>().blocksRaycasts = true;
ThisWindow.GetComponent<CanvasGroup>().interactable = false;
ThisWindow.GetComponent<CanvasGroup>().blocksRaycasts = false;
changing = false;
}
}
}
この実装で動作させたのが以下になります。Gifなので少しわかりにくいかもしれませんが、フェードインしてくるようなイメージです。

そろばんによる計算
計算値の出力
STARTを押すと問題が始まります。指定した桁数の問題が指定した問題数だけ続きます。そろばんを入力すると、それに同期して計算値が出力されるようになっています。

これは、問題の答えを出力するTextがUpdate()でそろばんに入力される値をポーリングして見張っています。値が変化するたびにコールバックを受ける方が作りとしてはよいかと思いますが、そもそもそろばんしか動いていないので、ポーリングで妥協しました。
void Update()
{
if (!_isLocked) {
int value = Soroban.GetComponent<Soroban>().GetValue();
this.GetComponent<TMP_Text>().text = value.ToString();
_answerTemp = value;
}
}
そろばんオブジェクトにアタッチされたSorobanスクリプトのGetValue()関数で値を取得して、textを更新しています。
次の問題へ遷移
NEXTを押すと次の問題に遷移します。問題はScrollViewを水平方向にスクロールさせて表示させています。STARTの段階で問題を作成して、Contetにまとめています。

ボタンが押されたタイミングでScrollRectのhorizontalNormalizedPositionを段階的に変化させて自動でスクロールしています。horizontalNormalizedPositionは0から1の範囲でスクロール量を指定できるパラメータになります。
void Update()
{
if (scrollon) {
scroll.GetComponent<ScrollRect>().horizontalNormalizedPosition += scrollinterval;
if (scroll.GetComponent<ScrollRect>().horizontalNormalizedPosition - _start >= scrollval) {
scroll.GetComponent<ScrollRect>().horizontalNormalizedPosition = _start + scrollval;
scrollon = false;
scroll.GetComponent<ScrollRect>().horizontal = false;
if(_isLast) {
nextButton.SetActive(false);
checkButton.SetActive(true);
}
}
}
}
問題数によって、スクロールすべき幅が異なるので、割合も異なってきます。事前に1問分のスクロール量がどれだけになるか計算してscrollvalに保存しておきます。また、1回のUpdate()でどれだけ動かすのかも計算してscrollintervalに入っています。scrollintervalを小さくすれば、スクロール量は小さくなるので、細かくなりますが、遅くなります。逆にscrollintervalを大きくすればスクロールは早くなります。
ここもホントはアニメーションが使えるのかもしれませんが、今その力はないので、にわか仕込みのUpdate()で対応します。
最後の問題
最後の問題はNEXTボタンがFINISHボタンに代わります。これはTEXTだけを変えているのではなく、ボタンオブジェクトそのものを変更しています。先ほどのスクロールのコードの
if(_isLast) {
nextButton.SetActive(false);
checkButton.SetActive(true);
}
この部分です。最後のスクロールが完了したタイミングでSetActiveでボタンを入れ替えています。ここで、スクロールが完了する前にボタンをSetActiveで入れ替えた時、処理中のものがあっても、強制終了となる点に注意が必要です。見えなくなるだけでなく、オブジェクトそのものが機能しなくなります。
ただし、オブジェクトの存在自体はなくならないので、再度SetActiveで有効にすれば、復活します。ボタンを入れ替えると以下のようになります。普通です。

結果画面
結果は先日購入したUI Packを色々使ってみたかったので、ポップアップWindowにしました。〇×表記はいい案が浮かばなかったので、苦肉の策です。まぁ、結果がわかればいいということで。


結果画面からは、全ての問題をやり直す、間違えたところだけやり直す、まったく最初からやるの三択が選べます。
まとめ
非常に単純ですが、やりたいことが一通りできたので、いったん完成とします。さらっと記事にしていますが、裏ではボタンが消えたり、画面が出てこなかったり、そろばん珠が暴れたり、いろいろ苦労しました。。ただ、高々2週間程度でこのようなアプリを実装できるUnityはほんとよくできているなぁと感心しています。これが無料で触れるとは、ほんといい時代だなぁと思います。
コメント