Hatena::ブログ(Diary)

土屋つかさのテクノロジーは今か無しか

2017-06-27

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

 その時、Inputのセッティングでは「Submit」にマウスの左ボタンを追加していました。
f:id:t_tutiya:20170627154512p:image
 これは、最初期に特に深い考えもなく、「スペースキーマウスの左クリックを両方「決定」にしたら楽でいいじゃん」くらいの気持ちで設定した物でした(そしてすっかり忘れていました)。そして、これこそがバグの原因だったのです。

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

 もう一つの条件とは、そのボタンがフォーカスされている状態で、EventSystem(Canvas上のオブジェクトのイベントを管理しているGameObject)のStandaloneInputModuleコンポーネントで、Submit Buttonに設定された物理ボタンが押された場合です(Buttonの上で押下される必要すらありません)。
f:id:t_tutiya:20170627154510p:image
 Submit Buttonには、デフォルトでは"Submit"仮想キーが設定されています。この状態で、初回にボタンをクリックすると、まず以下の状態になります。

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

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

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

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

今回の反省

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

2017-06-24

Unity Tips(4:Unityマニュアル誤訳編)

 Unityマニュアルアニメーションの項は所々「誤訳では?」という所があるので気づいた所について。まあ、これだけの分量を日本語で読めるだけでもありがたい話ではあります。
 以下幾つかの項目に私訳をつけていますが、各自の責任で運用してください。

AnimatorController の作成

https://docs.unity3d.com/ja/current/Manual/AnimatorControllerCreation.html
・「プロジェクトビューの上で右クリックして’Create > Animator Controller’を選択。」が重複している。
・片方は「プロジェクトビューのCreateメニューからAnimator Controllerを選択(From the Project View by selecting ‘Create > Animator Controller’.)」が正しい。

アニメーションステート

https://docs.unity3d.com/ja/current/Manual/class-State.html
Write Defaultsオプション説明に誤訳がある。

誤:

AnimatorStates を書くかどうかにかかわらず、その Motion によってアニメーション化されてないプロパティーによってデフォルト値に戻します。

私訳:

AnimatorStateのモーション時にアニメーションしない時、プロパティデフォルト値に書き戻すかどうか

原文:

(https://docs.unity3d.com/ja/540/ScriptReference/Animations.AnimatorState.html
Whether or not the AnimatorStates writes back the default values for properties that are not animated by its Motion.

参考サイト

Unity】知らないと面倒くさい事になるかもしれないAnimatorの「Write Defaults」の動作について
http://tsubakit1.hateblo.jp/entry/2017/01/15/233000

アニメーション遷移

https://docs.unity3d.com/ja/current/Manual/class-Transition.html
Animation StateのExit Timeオプション説明の最初の段落に誤訳がある。

誤:

 Has Exit Time が有効な場合、Exit Time の値は遷移が起こる正確な時間を表しています。これは正規化された時間で表され、そのため、例えば、Exit Time 0.75 は、最初のフレームで 75% アニメーションが再生された時点を意味し、Exit Time の状態は true です。次のフレームでは、状態は false です。

私訳:

 Has Exit Timeが有効な場合、この値は遷移が開始する正確な時間を表します。これは正規化された時間で表されます(例えば、exit timeが0.75であれば、そのアニメーションの再生が75%に到達した最初のフレームで、Exit Time条件(condition)が有効になることを意味します)。次のフレームに、条件は無効になります。

原文:

If Has Exit Time is checked, this value represents the exact time at which the transition can take effect. This is represented in normalized time (for example, an exit time of 0.75 means that on the first frame where 75% of the animation has played, the Exit Time condition is true). On the next frame, the condition is false.

補足

※条件(condtion)というのは、同ページ下部にある、遷移開始条件のこと。
※Exit Timeオプション説明についてはこちらがわかりやすい
Unity開発】Animator Controllerの遷移設定(Duration等)【ひよこエッセンス
http://hiyotama.hatenablog.com/entry/2015/06/29/090000

メッシュのインポート設定

https://docs.unity3d.com/ja/current/Manual/FBXImporter-Model.html
Generate Collidersプロパティ説明の箇所にSwap UVsの説明が入っている(しかもSwap UVsとは違う翻訳になっている)。

私訳:

これを有効にすると、メッシュは自動的にアタッチされたメッシュコライダーと共にインポートされます。環境ジオメトリのためにコリジョンメッシュを簡単に作るのに役立ちますが、動かすジオメトリでは避けた方が良いでしょう。

原文

https://docs.unity3d.com/550/Documentation/Manual/FBXImporter-Model.html):
If this is enabled, your Meshes are imported with Mesh Colliders automatically attached. This is useful for quickly generating a collision Mesh for environment geometry, but should be avoided for geometry you are moving.

Playable API

https://docs.unity3d.com/ja/current/Manual/Playables.html
これも誤訳ではないのだけどわかりにくいので。
こことその下のページで「スクリプトリファレンス(API)の」のように書かれている箇所は"In the Script Reference (API)"の訳なのだけど、これは恐らくUnityではライブラリのことを「スクリプトリファレンス」と読んでいるのでしょう。なので全部「APIの」的に読み替えるとエンジニアは分かりやすいかと思います。

アートアセットベストプラクティスガイド

https://docs.unity3d.com/ja/current/Manual/HOWTO-ArtAssetBestPracticeGuide.html
マテリアル」の項で、「ネイティブパッケージのマテリアルの設定は Unityインポートされません。」という文章が2回出てくる。

私訳:

貴方がUnityマテリアルを作る際は「<モデル名>-<マテリアル名>」か「<テクスチャ名>」の好きな方を選ぶと良いでしょう。

原文

https://docs.unity3d.com/550/Documentation/Manual/HOWTO-ArtAssetBestPracticeGuide.html):
You can choose to create Materials in Unity from either:<modelname> - <material name> or <texture name> Make sure you know which one you want.

2017-06-23

Unity Tips(3)


Tips」と書いてはありますけど、単に土屋が導入時に気になった所をメモしてるだけです

用語アトラス

・複数の画像を1つの画像にまとめたもの。
・複数の画像をアトラスにする作業を「アトラス化」とも言う。
UnityではSprite Packerなどで作成したSpriteを指す。
atlasはこの場合「図表集」の意味。

spriteについて(1)

Unityマニュアルスプライト
https://docs.unity3d.com/ja/540/Manual/Sprites.html
・「スプライトツール」の項で、「Sprite Creator」「Sprite Editor」「Sprite Renderer」「Sprite Packer」が紹介されているが、「Sprite Renderer」だけはツールではなくコンポーネントクラス(他の3つはUnity上で操作するエディタツール)。
・英語版のマニュアルの「Sprite Tools(https://docs.unity3d.com/550/Documentation/Manual/Sprites.html)」も同じ構成なので、「スプライト(を操るため)の道具たち」くらいの意味なのかもしれない。
・余談だけどその下の「スプライトインポートと設定」で連番がおかしくなってる。

プレハブについて

・プレハブをInstantiateで生成すると、"xxxxx(clone)"というGameObjectがヒエラルキービューに表示される。このプレハブはplay modeの間だけシーンに存在するオブジェクトとなり、select/revert/applyのボタンが表示されない。
・このオブジェクトにAddされているコンポーネントの値をInspector上で変更すると、その変更は同じプレファブから生成されたクローン全てにおよび、同時にプレハブの値自体も更新され、保存される。
・編集を避けたい場合は、以下のように編集禁止フラグを立てる。ただし、子要素のGameObjectには伝搬しないので、その場合は手作業で走査する必要がある。

TargeGameObject.hideFlags = HideFlags.NotEditable;

→うーん、チームの誰かが意図せず変更して事故る未来が見えるな……。apply押してない(そもそもボタンが出てない)のに元のプレハブのデータが更新されるっておかしいと思うんだけど、土屋がなにか間違ってるのかなあ……?
参考: http://tsubakit1.hateblo.jp/entry/20140422/1398177224

Unityちゃん同梱のスクリプトコンパイルすると警告が出る箇所の修正方法

スクリプトが旧形式のAPIを叩いているので、警告が出ている3ファイルを以下のように修正(エラーが出ていても動作には問題ない)。
\Assets\Resources\UnityChan\SplashScreen\Scripts\SplashScreen.cs(1か所)

//修正前
Application.LoadLevel (Application.loadedLevel + 1);
//修正後
SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex + 1);

\Assets\Resources\UnityChan\Scripts\IdleChanger.cs(2か所)

//修正前
previousState.nameHash != currentState.nameHash
//修正後
previousState.fullPathHash != currentState.fullPathHash

\Assets\Resources\UnityChan\Scripts\UnityChanControlScriptWithRgidBody.cs(4か所)

//修正前
currentBaseState.nameHash
//修正後
currentBaseState.fullPathHash

アセットの登録方法について

・Assetの登録自体は、Assetsフォルダ配下に直接配置すれば自動的に取り込まれるので、手作業インポートする必要はない
・ただし、画像はTextureコンポーネントとして認識されるので、必要なら主導でspriteコンポーネントに変更する必要がある(エディタ拡張でいけるか?)

2017-06-22

Unity Tips(2)

".mat"ファイルがショートカットとして認識されてしまう。

 マテリアルデータを格納する".mat"ファイルが「Microsoft Office Access Table Shortcut」に関連付けられていて拡張子が表示されない場合(TortorizeGitで操作できなくて困った)。

対応

 「コントロールパネル」>「プログラム」>「あるファイルの種類を特定のプログラムでオープン」で".mat"をエディタに関連付ける。

ユニティちゃんがピンク色になった時の対処

 ユニティちゃん(UnityChan_1_2_1.unitypackage)をResources配下にインポートし、"Resouces/UnityChan/Models/unitychan"プレハブを動的に生成する際、winビルドするとモデルがピンク色に出力されてしまった時の対処

注意

 これ、根本的な修正になっているのかは分かりません(土屋の3Dプログラミングへの理解不足から来てるのかもしれないです)。ひとまずこれで治ったという話。

原因

 *.shaderファイルの中でCharaSkin.cgをインクルードする箇所がことごとくコンパイルエラーを吐いており、shaderが読み込まれないためにInternalErrorShaderが設定され、ピンク色になっている

対応

 各*.shaderファイルの該当箇所を以下のように修正する

//修正前
#include "CharaSkin.cg"
//修正後
#include "./CharaSkin.cg"

 ※モデルがピンク色になっていたら基本的にシェーダーか、シェーダーへの参照が壊れているとみて良い。参考→http://tsubakit1.hateblo.jp/entry/2017/03/30/090000

追記(2017/06/23)

 ユニティちゃんシェーダー (Unity 5.4/5.5β 対応版)が、上記のピンク化対応パッチらしい(shaderフォルダ内のreadme参照)。
 適用すると、"*.cg"の代わりに".cginc"ファイルがインクルード対象となり、上記の修正なしでも正常に表示される(既に上記の修正をしている場合は、一度シェーダーを再コンパイルする必要があるかもしれない)。

TortoriseGitにファイルを登録しても管理アイコンが出ない場合

 Unityとは直接関係ありませんが環境構築中に起きたこと。アイコンオーバーレイは全サービスを合わせて合計で15個までしか適用されないようで、Dropboxインストール済みのマシンにTortoriseGitをインストールするとレジストリ上後ろに回されたTortoriseGitのアイコンオーバーレイがスキップされます(下記サイト参照)。

TortoiseGitでアイコンオーバーレイ icon overlayが表示されない 解決方法
https://urashita.com/archives/4535
TortoiseGitのアイコンオーバーレイが表示されないときの対処法3つ
http://shinten-dream.com/computer/49/

 土屋の環境ではキー名を変えた上でダブルクオーテーションで囲う方法で解決しました(レジストリエディタでの編集には管理者権限が必要。ご自身の責任でどうぞ)

Canvasについて(1)

 Render Modeを"Screen Scene - camera"にした時、Render Cameraをスクリプトから設定したい場合、Canvas.worldCameraに対象のカメラを設定する。

GameObject.Find("CanvasName").GetComponent<Canvas>().worldCamera = targetCamera;

参考:Unity4.6のuGUIのCanvasのRender Cameraをスクリプトで変更したい
https://ja.stackoverflow.com/questions/5008/unity4-6%E3%81%AEugui%E3%81%AEcanvas%E3%81%AErender-camera%E3%82%92%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%A7%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%9F%E3%81%84

2017-06-21

Unity Tips(1)

 お仕事でゲームエンジン研究をさせてもらえる事になりまして、現在Unityの勉強中です。ラーニング中で気づいたことや、はまったことについて雑多に書いていきます。間違っている個所もあるかと思うので指摘してもらえると助かります(いずれまとめられたらいいな)

GameObjectとはなにか

・UnityEngine.GameObjectクラスのインスタンス(sealdで派生クラスは作れない)
・Transformコンポーネントクラスをデフォルトで保持している(なのでTransformコンポーネントは破棄できない)
・GameObject自体は機能を持っておらず、コンポーネント(MonoBehaivior派生クラス)を追加して機能を構成する(Cubeなどのデフォルトのゲームオブジェクトも同様)。

コンポーネントとはなにか

・ゲームオブジェクトにアタッチ可能なクラスインスタンスのこと。
・UnityEngine.Componentクラスの派生クラスはすべてコンポーネントになる。
・MonoBehaviourクラスもComponentクラス(正確にはComponentから派生したBehaviourの派生クラス)。
スクリプトはMonoBehaviourを継承することでコンポーネントになる。

Projectに登録してあるアセット(or プレハブ)を動的に生成する方法

//Resoruces/folder配下にあるCubeプレハブを生成する例
GameObject Cube = Instantiate(Resources.Load("folder/Cube", typeof(GameObject))) as GameObject;

Resources.Load
https://docs.unity3d.com/jp/540/ScriptReference/Resources.Load.html
・\Assets\Resources\フォルダ配下のアセットを読み込める
・Textureなどの非コンポーネントオブジェクトを読み込むことができる。本来は読み込んだアセットをAddCompornentするための機能
・ちなみに、Instantiateメソッドを単体で使う場合は以下のようにして、Inspector経由でアセットを設定する。

public Transform myCube;//Inspectorからアセットを設定する。

void Start () {
    Instantiate(myCube, transform.position, transform.rotation);
}