#概要
この記事は前回の記事の続きです。
【Unity】UGUIでuiの上にカーソルが重なったらメッセージを出す
#前回の反省点
前回の記事だと以下の問題点がありました。
1.メッセージを出したいオブジェクトすべてにメッセージで使うTextを設定しないといけない
2.メッセージを表示したくないゲームオブジェクトのUiのRaycastTargetのチェックを外さないといけない
今回は前回使っていたIPointerEnterHandlerとIPointerExitHandlerを使わずにやったことをまとめようと思います。
使わない理由は前回のやり方のままだとメッセージを表示したくないuiのRaycastTargetをはずさないといけない問題が解決しないためです。
#今回使うもの
・EventSystem.RaycastAll
第一引数で渡した情報をもとにRayを作成し、あたったuiの情報を第二引数で渡したRaycastResult配列に格納してくれます。
#解説
クラスはこういう感じになりました。
public class AssistanceMessageManager : MonoBehaviour
{
//前フレームでヒットしたヘルプが必要なオブジェクト
private GameObject _preFrameAssistanceObject =null;
//uiにRayがあたったか結果格納リスト
private readonly List<RaycastResult> rayResult = new List<RaycastResult>();
[SerializeField] private Text _helpMessage;
void Update () {
rayResult.Clear();
var currentPointData = new PointerEventData(EventSystem.current);
currentPointData.position = Input.mousePosition;
EventSystem.current.RaycastAll(currentPointData,rayResult);
//Rayがあたったオブジェクトから目的のインターフェースを持ったものがあればtextを更新
if (_preFrameAssistanceObject == null)
{
foreach (var raycastResult in rayResult)
{
var assistance = raycastResult.gameObject.GetComponent<INeedAssistance>();
if (assistance != null)
{
_preFrameAssistanceObject = raycastResult.gameObject;
_helpMessage.text = assistance.assistMessage;
_helpMessage.transform.position = currentPointData.position;
_helpMessage.transform.gameObject.SetActive(true);
break;
}
}
}
else
{
//前フレームにあたったオブジェクトがヒットしていなければuiから離れたとみなして非表示にする
if (rayResult.All(ray => ray.gameObject != _preFrameAssistanceObject))
{
_helpMessage.transform.gameObject.SetActive(false);
_preFrameAssistanceObject = null;
}
}
}
private void OnDestroy()
{
rayResult.Clear();
_preFrameAssistanceObject = null;
}
}
public interface INeedAssistance
{
string assistMessage { get; }
}
処理は以下の手順で行っています。
1.EventSystemからRayに必要な情報を作り、それを用いてRayがUiにあたっているかどうかを見る
↓
2.当たっていなおかつ特定のインターフェースを実装したコンポーネントがあればメッセージを表示し、キャッシュとしてあたったゲームオブジェクトを保持しておく
↓
3.次のフレーム時に再びRayを飛ばし、前フレームでキャッシュしたゲームオブジェクトがあってなおかつ今回のRayに引っかからなかったら前フレームに当たったuiから離れたとみなしメッセージを非表示にする
↓
4.以下1~3の繰り返し
今回の対応で上記に上げた問題点が以下のように解決しました。
1.メッセージを出したいオブジェクトすべてにメッセージで使うTextを設定しないといけない
これはAssistanceMessageManagerで管理するようにしたため解決しました。
こちらのほうがメッセージを出したいオブジェクトが増えてもそのオブジェクトにTextの設定が必要なくなったため楽になりました。
2.メッセージを表示したくないゲームオブジェクトのUiのRaycastTargetのチェックを外さないといけない
これについてはRaycastAllでヒットしたオブジェクトから特定のインターフェースを持つオブジェクトを対象に呼び出すようにしたため解決しました。
インターフェースにしてる理由は今後汎用的に使うことを考えると、特定のコンポーネントを持っているかで判断するよりもインターフェースのほうが依存しなくなるためです。
#まとめ
EventSystem.RaycastAllを使うことによって、IPointerEnterやIPointerExitHandlerを使わずにメッセージ表示を実現できました。
EventSystemについてまだ知らないことが多いので、調べてまとめたいと思います。
#参考