色々と手探りで情報を集めながら、そろばんアプリの完成に向けて、悪戦苦闘しています。今回はそろばんアプリがどこまでできたかを、苦戦した点を踏まえながら、紹介させていただきます。
そもそもなぜ始めたか
そもそも、そろばんアプリを作ろうと思ったのは、子供がそろばんを習っており、アプリを作ってびっくりさせてやろうと思ったからです。Unityを選んだ理由も、そろばんの動きが物理演算で自然にできるのでは?という単純な考えからです。
ただ、比較的レイヤの低い組み込みエンジニアにとって、こういったアプリ開発は斬新で、勉強になります。自分のスキルの幅を広げるのにもよい機会となっています。
ここまでのできばえ
どうでしょう?まだUIはありませんが、そろばんらしくなっています。Blenderで悪戦苦闘しながら作成したそろばん珠も手前みそながら、結構いい感じになっていると思います。左側には計算式を表示させるようなUIを考えています。
動かし方にもこだわりました。他のそろばんアプリをダウンロードして色々動かしてみましたが、どれもご入力が起こりやすく、イライラしました。そろばん有段者の私もニッコリの操作性に仕上がってきました。
苦労した点
苦労したり工夫した点について五月雨式にはなりますが、サンプルコードも合わせながら、ご紹介していきます。
そろばん珠の動かし方
以前こちらの記事で物理演算を用いたそろばん珠の動かし方についてご紹介しました。
AddForceを使って、そろばん球に力を加えて、動かす手法です。最初はこれを改良してそろばん珠の動きを表現するつもりでした。けど、素人には珠が暴れまわって、かなり制御が困難でした。
また、物理演算で動かしたとしても、珠が入力されている状態か、入力されていない状態かの状態に倒してあげる方が扱い安いと思うので、結局直接オブジェクトの座標をいじる必要がありました。
それらを踏まえて、タップの動きに追従する方がコントロールしやすいということになり、物理で動かすことは断念しました。。(この辺から、別にUnityである必要性はなくなってきてます。。)結局以下の記事にある手法をとっています。
そろばんのプレハブ化
そろばんの一桁のパーツはSorobanPartsとしてプレハブ化して扱っています。
そしてSorobanPartsを横に並べることで、そろばんを形成しています。1の位には白い丸がつくのですが、それはSorobanPartsを子に持つSorobanObjectで決めてつけています。下の枠内のCylinderがそれにあたります。
プレハブについては以下の記事にまとめています。
そろばん珠のスクリプト
そろばん珠のスクリプトは五円珠と一円珠で分けてあります。それは五円珠は単体で動けますが、一円玉は他の珠と連携して動く必要があるためです。一円珠のスクリプトは以下のようにUnity上で珠の下限座標、上限座標、上から何番目かのindexをパラメータを設定できるように書いてあります。
public class sorobanDama1 : MonoBehaviour, IPointerEnterHandler, IPointerDownHandler, IPointerUpHandler
{
public float MIN_LOCAL_Y;
public float MAX_LOCAL_Y;
public int MY_INDEX;
private float MIN_LOCAL_THRESH_Y = 0f;
private float MAX_LOCAL_THRESH_Y = 0f;
これにより、珠に応じた座標をUnity上で入力することで、同一のスクリプトで実装できるようにしてあります。
一円珠は起動時に同じ桁にある全ての一円珠のTransformコンポーネントとScriptコンポーネントを取得して配列に格納しています。動かされる一円珠によって、他の一円珠を連動して動かすかどうかを判断して、必要であればこのTransformを変更したり、Scriptで情報を設定したりして、連携をとっています。
親が持つ子供のオブジェクト取得はparent.GetChild()で上から順に取得でき、それぞれGetComponentでコンポーネントを取得しています。
void Start()
{
for (int i = 0; i < 4; i++) {
_damaTransforms[i] = _transform.parent.GetChild(i).GetComponent<Transform>();
_damaScripts[i] = _transform.parent.GetChild(i).GetComponent<sorobanDama1>();
}
}
そろばんの操作性
操作性は有段者としてかなりこだわりを持ってやろうと思っていました。そろばんは人差し指と親指で入力するので、マルチタップ入力は必須になります。また、タップで珠を選ぶ→動かすではなく、珠の下や上からスワイプして動かすように入力するのがそろばんの扱いに近いと気づきました。
なので、スクリプトで受けるイベントはOnPointerDown()やOnPointerUp()で制御するのではなく、OnPointerEnter()を使いました。これにより、オブジェクトの外からスライドさせてきたタップを拾うことができます。
タップを拾った後は更新のためのフラグを立てて、Update()で場所を更新していきます。これは以下の記事で概要を紹介しています。
また、マルチタップの際、先にタップされている方の指が離されるとInput.GetTouch()で指定すべきインデックスが変更されるという現象が確認されました。例えば、一円珠タップ>五円珠タップ>一円珠解放 とした場合、その後五円珠で取得しないといけないタップ情報はInput.GetTouch(1)ではなくInput.GetTouch(0)になるということです。Input.GetTouch(1)を指定すると飛んでしまっていました。
インデックスと指は紐づいているのではなく、あくまでその時のスナップショットのようです。この問題はコードに以下のようにパッチを当てることで対応しました。
void Update()
{
// マウスが押下されている時、オブジェクトを動かす
if (_isSelected) {
if (_touchNum > Input.touchCount) {
// タッチの数が減っている時は、前にタップされていた指が解放された時
// 見るべきIndexを一つ減らす
_touchIndex--;
_touchNum = Input.touchCount;
}
touchtmp = Input.GetTouch(_touchIndex);
Update()でタッチの情報を取得する前にカウント数を確認して自分がタッチされた時よりも数が減っている時は一つ前の情報を取得するというものです。これでタップの誤動作が改善しました。
まとめ
色々と躓きながらですが、そろばん本体は形になってきました。また、いろいろと記事にして頭を整理することで、自分の身にもついてきているような気がします。
今後はUI部分を実装して、アプリの完成を目指していこうと思います。
コメント