土屋つかさの技術ブログは今か無しか

土屋つかさが主にプログラミングについて語るブログです。

ButtonのOnClickイベントが2回発生している(ように見える)バグに悩まされた話

 コードを書いていて、どうにもおかしな挙動に悩まされた時の話。結論から言うと土屋の設定ミスだったんですが、Unityらしいハマり所と思ったので紹介しておきます。

不具合発生:3つのボタンのOnClickイベントが意図しない挙動をする。

 canvas上にButtonを3つ出し、それぞれのOnClickイベントを取得しようとした時、おかしな挙動に悩まされました。

 それは、ボタンをクリックするたびに、対応するモデルがアニメーションするデモを作っていたのですが、なぜか「ボタンをクリックすると(初回を除いて)「1つ前の」ボタンのOnClcikが発生する」という謎現象でした。

問題の切り分け:同じボタンのOnClickイベントが2回発生している?

 なにがバグっているのかを確認するためにテスト用のシーンを作り、ボタンを一つだけにして、OnClickの挙動を確認した所、より奇妙なことが起きている事に気づきました。

 初回のクリック:①MouseDown→②MouseUpの後でOnClickが発生する
 2回目以降のクリック:③MouseDown/④MouseUpの両方でOnClickが発生する

 ボタンを増やしてみた所、どうやらこの③のタイミングで発生するOnClickの元になるボタンが、一個前にクリックされたボタンの物で、そのせいで一個前のボタンが反応しているようでした。

原因判明

 と、ここまでわかったのですが、その原因がさっぱりわかりません。正直、「これUnityのバグなのでは?」とさえ思いましたw

 そこで、グーグル先生に相談した所、海外のUnity公式コミュニティで、(ほぼ)まったく同じ問題で悩んでいる人がいて(この人も「もしかしてUnityのバグ?」と書いていましたw)、別の方が「いやそれ俺も以前陥ったけど設定ミスだから」と、不具合の原因と対処法を提案していました。

Button OnClick Event is firing twice
https://forum.unity3d.com/threads/button-onclick-event-is-firing-twice.359933/

原因と対処法

 原因は、Project Settings>Inputを弄っていたことにありました。

 Unityには「仮想キー」という仕組みがあり、複数のキーに同じ名前を設定して仮想的なキーとして扱うことができます。例えば、「スペースキー」と「マウスの左ボタン」と「ジョイパッドの1ボタン」をまとめて「Fire1」という名前を設定することで、スクリプト側からはユーザーが使っているデバイスを意識せずに処理を作れるわけです。

 その時、Inputのセッティングでは「Submit」にマウスの左ボタンを追加していました。

 これは、最初期に特に深い考えもなく、「スペースキーとマウスの左クリックを両方「決定」にしたら楽でいいじゃん」くらいの気持ちで設定した物でした(そしてすっかり忘れていました)。そして、これこそがバグの原因だったのです。

 さて、ButtonのOnClickイベントは、当然そのボタンがクリックされた時に発生するのですが、実は、他にも発生するもう一つの条件があります。

 もう一つの条件とは、そのボタンがフォーカスされている状態で、EventSystem(Canvas上のオブジェクトのイベントを管理しているGameObject)のStandaloneInputModuleコンポーネントで、Submit Buttonに設定された物理ボタンが押された場合です(Buttonの上で押下される必要すらありません)。

 Submit Buttonには、デフォルトでは"Submit"仮想キーが設定されています。この状態で、初回にボタンをクリックすると、まず以下の状態になります。

・ボタンがクリックされたので、そのボタンのOnClickイベントが発生する
・ボタンがクリックされたことで、そのボタンがフォーカス状態になる

 そして、もう一度クリックすると、以下の処理が行われます

・ボタンがフォーカスされた状態で、"Submit"仮想キーに設定されたボタン("mouse 0")がクリックされたので、OnClickイベントが発生する
・ボタンがクリックされたので、そのボタンのOnClickイベントが発生する

 というわけで、OnClickが意図せずに2回発生していたのでした。対処法としては簡単で、"Submit"仮想キーから"mouse 0"を外せばOKです。

今回の反省

 今回は完全に土屋の設定ミスによる物でした。仮想キーの設定を変えたことなどすっかり忘れていて、それが目の前の不具合の原因になっていたとは想像もしていませんでした。
 正直、uGUIのボタンがどうしてこんな実装になっているのかという疑問もありますが、それ以上に「インスペクター上で値を変更したことを忘れたまま開発が進み、それが潜在的なバグの原因となりうる」という事を体験し、今後の開発体制の構築に活かして行こうと思いました。