ボクステ公式ブログ

[Unity]EventSystem/EventTriggerの初回クリック不発原因
Unity6.2(Ver.6000.2.7f2)でのスクリプトを想定しているため、以下の記述例は
C# です。
EventSystemは、ImageなどのUI要素とともに用い、マウスポインターやボタン操作を感知する機能です。
現在開発中の縄文時代シミュレーションゲーム(以下、「縄文シミュ」)において、次のような不具合が起こっていました。
・クラフトパネルの分類タブのいずれかをクリックしてクラフト可能アイテムリストを更新すると、その後、最初のアイテムクリックが不発になる。
・2回目以降のクリックは正常に動作する。
・あらかじめアイテムアイコン上ではないリスト内領域をクリックしてからアイテムアイコンをクリックすると、初回でも正常に動作する

[他の一般的な不具合ではないことを確認]
・EventSystemがシーンに存在しているか?
・ImageオブジェクトにEvent Triggerをアタッチしているか?
・Imageの Raycas Target にチェックが入っているか?
・重なり合うImageやパネル要素のRaycastTargetにチェックが入っていないか?(入っているとレイキャストの判定を吸われ、ガードされてしまう)
・ButtonのOnClick()などと競合していないか?
「縄文シミュ」では、上記はすべて確認済みで、問題はありませんでした。
[クリック不発原因]
タブをクリックしてリストを更新した後、EventSystemのSelected(選択中状態)が残ったままになっているため、 最初のアイテムクリックが不発になると考えられます。
[解決方法]
リストを更新するメソッドの最後で、明示的にEventSystemのSelected(選択中状態)を解除すると最初のクリックが正常に動作します。
以下は、EventSystem.current.SetSelectedGameObject(null); を呼び出して、Selectedをリセットするスクリプト例です。
[選択解除コードはどこに書くか]
「縄文シミュ」特有の事情ですが……
1.カテゴリタブがクリックされると「クラフト総合管理」クラス内の「選択カテゴリ変更」メソッドが実行されます。
2.「選択カテゴリ変更」メソッドから外部クラス「道具リスト管理」「土器リスト管理」「建築リスト管理」いずれかの「リスト更新」メソッドが実行されます。
3.カテゴリ別「リスト更新」メソッドの最後に、選択解除のコードを入れると、タブ切り替え後、最初のクリックが正常に動作するようになりました。
なお、「クラフト総合管理」クラスの「選択カテゴリ変更」メソッドから直接、外部クラスの「リスト更新」メソッド実行とEventSystem選択解除を同時に行った場合は、 リスト更新後最初のクリックが不発となり失敗しました。
結論:実際にリスト更新を行うメソッド内の一番最後で、選択解除を行うと良い。
[EventSystemをスクリプトから扱う時の注意点]
選択対象としたい、または、選択を解除したいオブジェクトにImage(Event Triggerアタッチ済み)やButton等の選択可能なUI要素がアタッチされていることを確認します。
そもそも選択可能なUI要素がないとその操作ができないからです。
実際の「縄文シミュ」では、アイテムアイコンPrefabの親はUI要素のない空のオブジェクトで、Imageはその子要素であるため、 親オブジェクト経由で子オブジェクトやそのスクリプトを指定して選択状態を変更しています。
最初は、この親オブジェクトに対して選択解除を行おうとして失敗しました。
[最初から選択したいオブジェクトの指定]
パネルを開いた時やリストが更新された時、自動的に特定のオブジェクトを選択状態にしたい場合は、 EventSystem.current.SetSelectedGameObject(選択したいオブジェクト); として、引数に選択したいオブジェクトを指定します。
[動的生成オブジェトの最初の要素を選択する方法]
publicなGameObjectをインスペクターから指定する方式では、リスト更新の度に動的生成されるprefabゲームオブジェクトを選択することができないため、 他の方法を用います。

前提として、リスト更新メソッドではデータベースから取得した情報をもとにアイテムアイコンのprefabゲームオブジェクトを生成(Instantiate)しています。
foreachを使用して繰り返しオブジェクトを生成するため、最初のオブジェクトを別枠で記録しておく必要があります。
また、これも「縄文シミュ」特有なのですが、生成したオブジェクト側のスクリプトを取得し、 その中のメソッドを実行しないとUIにアイテム詳細が表示されず「最初から選択された」という風には見えないため、 リスト最初のアイテムを選択中にした上で、メソッド実行も行います。
【関連記事】
野良箱 | ゲーム制作のWIP(Work in progress)ログ
外部の個人ブログでは「縄文シミュ」開発過程で発生した不具合とその解決方法を逐次公開しています。
カテゴリ ゲーム開発
C# です。
EventSystemは、ImageなどのUI要素とともに用い、マウスポインターやボタン操作を感知する機能です。
現在開発中の縄文時代シミュレーションゲーム(以下、「縄文シミュ」)において、次のような不具合が起こっていました。
・クラフトパネルの分類タブのいずれかをクリックしてクラフト可能アイテムリストを更新すると、その後、最初のアイテムクリックが不発になる。
・2回目以降のクリックは正常に動作する。
・あらかじめアイテムアイコン上ではないリスト内領域をクリックしてからアイテムアイコンをクリックすると、初回でも正常に動作する

[他の一般的な不具合ではないことを確認]
・EventSystemがシーンに存在しているか?
・ImageオブジェクトにEvent Triggerをアタッチしているか?
・Imageの Raycas Target にチェックが入っているか?
・重なり合うImageやパネル要素のRaycastTargetにチェックが入っていないか?(入っているとレイキャストの判定を吸われ、ガードされてしまう)
・ButtonのOnClick()などと競合していないか?
「縄文シミュ」では、上記はすべて確認済みで、問題はありませんでした。
[クリック不発原因]
タブをクリックしてリストを更新した後、EventSystemのSelected(選択中状態)が残ったままになっているため、 最初のアイテムクリックが不発になると考えられます。
[解決方法]
リストを更新するメソッドの最後で、明示的にEventSystemのSelected(選択中状態)を解除すると最初のクリックが正常に動作します。
以下は、EventSystem.current.SetSelectedGameObject(null); を呼び出して、Selectedをリセットするスクリプト例です。
Sample.cs(C#)
using UnityEngine;
using UnityEngine.EventSystem; //EventSystemを使うための宣言
public class Sample : MonoBehaviour
{
public void ListUpdate()
{
//ここにリストを更新するメソッドの記述(省略)
EventSystem.current.SetSelectedGameObject(null);
}
}
このコードの意味は、「今は何も選んでいないよ」「選択中のオブジェクトはないよ」です。using UnityEngine.EventSystem; //EventSystemを使うための宣言
public class Sample : MonoBehaviour
{
public void ListUpdate()
{
//ここにリストを更新するメソッドの記述(省略)
EventSystem.current.SetSelectedGameObject(null);
}
}
[選択解除コードはどこに書くか]
「縄文シミュ」特有の事情ですが……
1.カテゴリタブがクリックされると「クラフト総合管理」クラス内の「選択カテゴリ変更」メソッドが実行されます。
2.「選択カテゴリ変更」メソッドから外部クラス「道具リスト管理」「土器リスト管理」「建築リスト管理」いずれかの「リスト更新」メソッドが実行されます。
3.カテゴリ別「リスト更新」メソッドの最後に、選択解除のコードを入れると、タブ切り替え後、最初のクリックが正常に動作するようになりました。
なお、「クラフト総合管理」クラスの「選択カテゴリ変更」メソッドから直接、外部クラスの「リスト更新」メソッド実行とEventSystem選択解除を同時に行った場合は、 リスト更新後最初のクリックが不発となり失敗しました。
結論:実際にリスト更新を行うメソッド内の一番最後で、選択解除を行うと良い。
[EventSystemをスクリプトから扱う時の注意点]
選択対象としたい、または、選択を解除したいオブジェクトにImage(Event Triggerアタッチ済み)やButton等の選択可能なUI要素がアタッチされていることを確認します。
そもそも選択可能なUI要素がないとその操作ができないからです。
実際の「縄文シミュ」では、アイテムアイコンPrefabの親はUI要素のない空のオブジェクトで、Imageはその子要素であるため、 親オブジェクト経由で子オブジェクトやそのスクリプトを指定して選択状態を変更しています。
最初は、この親オブジェクトに対して選択解除を行おうとして失敗しました。
[最初から選択したいオブジェクトの指定]
パネルを開いた時やリストが更新された時、自動的に特定のオブジェクトを選択状態にしたい場合は、 EventSystem.current.SetSelectedGameObject(選択したいオブジェクト); として、引数に選択したいオブジェクトを指定します。
Sample.cs(C#)
using UnityEngine;
using UnityEngine.EventSystem; //EventSystemを使うための宣言
public class Sample : MonoBehaviour
{
public GameObject selectedObject; //選択したいオブジェクト
public void ListUpdate()
{
//リストを更新するメソッドの記述(省略)
EventSystem.current.SetSelectedGameObject(selectedObject);
}
}
using UnityEngine.EventSystem; //EventSystemを使うための宣言
public class Sample : MonoBehaviour
{
public GameObject selectedObject; //選択したいオブジェクト
public void ListUpdate()
{
//リストを更新するメソッドの記述(省略)
EventSystem.current.SetSelectedGameObject(selectedObject);
}
}
[動的生成オブジェトの最初の要素を選択する方法]
publicなGameObjectをインスペクターから指定する方式では、リスト更新の度に動的生成されるprefabゲームオブジェクトを選択することができないため、 他の方法を用います。

前提として、リスト更新メソッドではデータベースから取得した情報をもとにアイテムアイコンのprefabゲームオブジェクトを生成(Instantiate)しています。
foreachを使用して繰り返しオブジェクトを生成するため、最初のオブジェクトを別枠で記録しておく必要があります。
また、これも「縄文シミュ」特有なのですが、生成したオブジェクト側のスクリプトを取得し、 その中のメソッドを実行しないとUIにアイテム詳細が表示されず「最初から選択された」という風には見えないため、 リスト最初のアイテムを選択中にした上で、メソッド実行も行います。
Sample.cs(C#)
using UnityEngine;
using UnityEngine.EventSystem; //EventSystemを使うための宣言
public class Sample : MonoBehaviour
{
public GameObject itemPrefab; //アイテムアイコンのprefab
public void ListUpdate()
{
GameObject firstObject = null;
//最初のオブジェクトを記録する変数
foreach(var item in itemList)
{
//リストを更新するメソッドの記述(省略)
GameObject go = Instantiate(itemPrefab);
//アイテムアイコンのprefabゲームオブジェクトを生成
if(firstObject == null)
{
firstObject = go; //最初のオブジェクトを記録
//以降firstObjectはnullではなくなるので記録されない
}
//(中略)
} //ここまでforeach
if(firstObject != null) //firstObjectがnullでない時
{
EventSystem.current.SetSelectedGameObject(firstObject);
//最初のオブジェクトを選択状態にする
var firstSend= firstCard.GetComponent<SendScript>();
firstSend.InfoSend();
//「クラフト総合管理」にアイテム情報を送信するメソッド
EventSystem.current.SetSelectedGameObject(firstObject);
//最初のオブジェクトを選択状態にする。
}
else
{
EventSystem.current.SetSelectedGameObject(null);
}
} //ListUpdate()メソッドここまで
}
※実際の「縄文シミュ」では、アイテムアイコンPrefab(親)はUI要素を持たない空のオブジェクトであるため、
それに対してEventSystemスクリプトを用いても選択状態を指定・解除することができません。UI要素を持つ子オブジェクトに対して同じことを行っています。using UnityEngine.EventSystem; //EventSystemを使うための宣言
public class Sample : MonoBehaviour
{
public GameObject itemPrefab; //アイテムアイコンのprefab
public void ListUpdate()
{
GameObject firstObject = null;
//最初のオブジェクトを記録する変数
foreach(var item in itemList)
{
//リストを更新するメソッドの記述(省略)
GameObject go = Instantiate(itemPrefab);
//アイテムアイコンのprefabゲームオブジェクトを生成
if(firstObject == null)
{
firstObject = go; //最初のオブジェクトを記録
//以降firstObjectはnullではなくなるので記録されない
}
//(中略)
} //ここまでforeach
if(firstObject != null) //firstObjectがnullでない時
{
EventSystem.current.SetSelectedGameObject(firstObject);
//最初のオブジェクトを選択状態にする
var firstSend= firstCard.GetComponent<SendScript>();
firstSend.InfoSend();
//「クラフト総合管理」にアイテム情報を送信するメソッド
EventSystem.current.SetSelectedGameObject(firstObject);
//最初のオブジェクトを選択状態にする。
}
else
{
EventSystem.current.SetSelectedGameObject(null);
}
} //ListUpdate()メソッドここまで
}
【関連記事】
野良箱 | ゲーム制作のWIP(Work in progress)ログ
外部の個人ブログでは「縄文シミュ」開発過程で発生した不具合とその解決方法を逐次公開しています。
カテゴリ ゲーム開発
