初めまして。今回ブログ記事を担当します、コンテンツ開発事業部の安井です。よろしくお願いします。
普段はUnityを用いたゲームアプリの開発を行っております。
今回は直近で行った実装に関してまとめようと思います。
行った内容はタッチ入力から座標を受け取り、画面内の指定されたGameObject
の識別です。さらに識別したものをフリック入力に合わせて追尾する仕組みを作成しました。
実装のサンプルを見るとわかりますがいくつか選択できるGameObject
のうちから一つを選び、フリックを追尾させるという実装物です。
機能の実現に当たって主に以下の二点に絞って実装を進めています。
・タッチ入力範囲の判定処理
・選択したGameObject
に座標を再設定する処理
タッチ入力範囲の判定処理
こちらの機能については以下のソースコードを書きました。
それぞれ入力時にTouchPhase.Began
かどうかを確認して呼び出しています。
public void SearchActiveCard(Vector2 touchArea) { //アクティブなカードの識別用変数 int activeIndex = 0; foreach (SingleCard card in cardList) { // カードの座標取得 RectTransform cardRect = card.gameObject.GetComponent<RectTransform>(); activeIndex++; // 渡した引数をキャンバス内のローカル座標に変換してもらう Vector2 localPoint = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle(cardRect, touchArea, null, out localPoint); // タップした箇所がカードの範囲内かの識別を行う if ((localPoint.x > cardRect.rect.xMin && localPoint.y > cardRect.rect.yMin) && (localPoint.x < cardRect.rect.xMax && localPoint.y < cardRect.rect.yMax)) { if (activeCard == card) isSameCard = true; if (activeCard != card) { isSameCard = false; ResetCardContentPosition(); } activeCard = card; activeCardNum = activeIndex; if (!isSameCard) { Vector3 pos = activeCard.GetContent().transform.localPosition; defaultPosition = new Vector3(pos.x, pos.y, pos.z); } return; } } }
処理の内容は入力された座標を比較対象の矩形のローカル座標へ変換を行い、最大・最小値の内側に収まっているかを見るというものです。
特に気を使っている点は画面から得た座標情報をCanvas
内にあるRectTransform
との比較を行えるように変換を行う工程です。
座標の変換には以下の関数を使用しています。
こちらはスクリーン空間上の座標情報を矩形の平面上にあるRectTransform
のローカル空間の位置に変換を行う関数です。
以下の部分が使っている個所の抜粋です。
Vector2 localPoint = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle(cardRect, touchArea, null, out localPoint);
関数に渡す引数に関して解説します。
第一引数にはローカル座標を探すためのRectTransform
を渡します。出力引数になっていることからも想像できると思いますが第四引数はここで渡したものに対するローカル座標になります。
第二引数は変換元の座標情報です。今回は画面から入力した座標がそれにあたります。
第三引数に関してはリファレンスによると「スクリーン空間位置に関連するカメラ」と記載されています。これだけ読んでもあまりピンと来ないかもしれません。
ここに渡すCamera
はCanvas
に設定したRender Mode
によって変わってきます。
第一引数に指定したRectTransform
が所属するCanvas
のRender Mode
の設定から以下二つのどちらかを渡せばいいようです。
・Screen Space - Camera
になっている場合にはRender Camera
に設定したCamera
・Screen Space - Overlay
になっている場合にはnull
これらの値が正しければ最終的に第四引数に算出したローカル座標の値が入っています。
最終的にlocalPoint
と比較対象のrect
との間でX・Y座標それぞれの比較を行い、矩形の範囲内に収まっているかの確認を行いました。
選択したGameObjectに座標を再設定する処理
位置の再設定の関数は以下のように書きました。
それぞれタッチからはTouchPhase.Moved
の確認を行い呼び出しています。
private void SetCardMovedVector(Vector3 newPosition) { if (activeCard == null) return; RectTransform cardRect = activeCard.gameObject.GetComponent<RectTransform>(); // 渡した引数をキャンバス内のローカル座標に変換してもらう Vector2 localPoint = Vector2.zero; RectTransformUtility.ScreenPointToLocalPointInRectangle(cardRect, newPosition, null, out localPoint); GameObject cardContent = activeCard.GetContent(); cardContent.transform.localPosition = localPoint; }
この実装ではスクロールビューの中のアイテムを移動させるという目的のために要素の子供を移動させるという作りにしました。
そのため入力箇所の特定でも使用したScreenPointToLocalPointInRectangle
を使い、入力座標を使い親から見たローカル座標を求めて再設定を行っています。
過去の座標を取得し差分を加算する処理での実装も考えたのですが、差分をローカル座標に変換を行い過去の座標に加算を行うという方法を行うのは煩雑になるため今回は避けました。
終わりに
今回ほかにも書けそうなネタはいくつかあったのですが一番困ったのがある程度可視化できる内容かどうかという点でした。そのため機能実装が目に見えやすい形にできるフリックで座標の再設定を行うという内容を選択しました。
個人的にもちょうどUnityで扱う空間の座標に関しても理解を深めるいい機会になったと感じました。
参考文献
UnityEngine.RectTransform - Unity スクリプトリファレンス UnityEngine.Rect - Unity スクリプトリファレンス UnityEngine.RectTransformUtility - Unity スクリプトリファレンス 【Unity】【uGUI】RectTransformUtilityでスクリーン座標をUIのローカル座標やワールド座標に変換する - LIGHT11