Canvasを使用してUnity3dでクリック可能な世界地図を作成する方法

世界の地図を収集するタスクがありました。 さらに、国はクリック可能にする必要があるため、多くの国、国、地域から収集することです。 はい、どこでも簡単です。あなたがしなければならないことは、地図全体を切り取り、ポリゴンコライダー、pfffを国全体に配置するだけです。 さらに、時間が経つにつれて、赤い点が国に表示されるはずです(はい、はい...あなたの考えを知っています)。 これらのポイントは、マップ上で非常に多くあるはずです。



Canvasを使用してマップを作成することが決定されました。 便利なことは、多くの時間を節約します。 しかし、今回はそうではありません。



最初の問題はサイズの異なる国であり、ある国が透明な地域または他の国で海を閉じ、クリックが必要な場所、より正確には、論理的に望ましい場所ではない場合に状況が発生します(スプライトを白塗りして、何が起こっているかの恐怖を見た)。



画像



最初に考えたのは、画像オブジェクトにボタンを掛けることは問題であり、帽子です。しかし、いいえ、問題は解決しません。ボタンは画像に基づいており、透明な領域をスキップしません。つまり、透明な領域はボタンのままです。

2番目の考え:押した時点で画像のピクセルを取得し、ピクセルが透明でない場合は、必要な場所をクリックし、透明であれば、押した時点で他のピクセルを確認します。



そして、これはst迷です。 画像のピクセルを取得する方法、これは問題ではありません、多くの例がありますが、クリックポイントでCanvasオブジェクトを取得する方法は? Canvasにはコライダーがないため、Raycastを使用不能にしても何も返されません。 そして、各画像国に野生コライダーポリゴンコライダーを押し込む。



さて、ヘルプを読んで、YouTubeで英語を話すインド人とマニュアルビデオを見て、EventSystemの機能を使用する時が来たという結論に達しました。



CountryMap国用のスクリプトを作成し、上記のネームスペースに含まれているIPointerClickHandlerインターフェイスから継承しました。 このインターフェイスOnPointerClickの唯一のメソッドは、PointerEventData型の変数を入力として受け入れます。 この変数から多くの興味深い情報を取得できますが、クリックの位置のみが必要です。



さて、国はクリック可能です(インターフェースのおかげです)、タップ位置はわかっていますが、この位置の下に画像のピクセルを取得することは残ります。 小さなメソッドを書きます:



private bool IsAlphaPoint(PointerEventData eventData) { Vector2 localCursor; RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(), eventData.position, eventData.pressEventCamera, out localCursor); Rect r = RectTransformUtility.PixelAdjustRect(GetComponent<RectTransform>(), GetComponent<Canvas>()); Vector2 ll = new Vector2(localCursor.x - rx, localCursor.y - ry); int x = (int)(ll.x / r.height * CountryImg.sprite.textureRect.height); int y = (int)(ll.y / r.height * CountryImg.sprite.textureRect.height); if (CountryImg.sprite.texture.GetPixel(x, y).a > 0) return false; else return true; } public void OnPointerClick(PointerEventData eventData) { if(!IsAlphaPoint(eventData)) { print(gameObject.name); } }
      
      





興味深い場合は、さらに詳しく説明します。 つまり、画面座標から画像のローカル座標に位置を変換し、画像の中心に対する位置を取得し、画像のピクセル座標を計算し、ピクセルを取得し、アルファチャネルを確認します。



すべての火、走れ!



画像



テクスチャの読み取りが禁止されています。画像のスプライトを見つけ、次のパラメータを設定します。



画像



しかし、今ではすべてが順調です...



2番目の問題。 アルファチャネルを特定するピクセルを見つけました。 しかし、透明な層の下には、まだ別の国があります。



繰り返しになりますが、EventSystemは、ブラックジャックとgameObjectaを備えた独自のレイキャストを備えています。



  List<RaycastResult> raycastResults=new List<RaycastResult>(); EventSystem.current.RaycastAll(eventData, raycastResults);
      
      





オブジェクトのリストを取得しました。これで作業できるようになりました。



  public void MayBeYouWantClickMe(List<CountryMap> ResultsCountryMap, PointerEventData eventData) { if (!IsAlphaPoint(eventData)) { print(gameObject.name); if (TapEvent != null) TapEvent(this); } else { ResultsCountryMap.Remove(this); if (ResultsCountryMap.Count > 0) ResultsCountryMap[0].MayBeYouWantClickMe(ResultsCountryMap, eventData); } } public void OnPointerClick(PointerEventData eventData) { if(!IsAlphaPoint(eventData)) { print(gameObject.name); if (TapEvent != null) TapEvent(this); } else { List<RaycastResult> raycastResults=new List<RaycastResult>(); EventSystem.current.RaycastAll(eventData, raycastResults); List<CountryMap> ResultsCountryMap = raycastResults.Select(x => x.gameObject.GetComponent<CountryMap>()).ToList(); ResultsCountryMap.RemoveAll(x => x == null || x.gameObject==gameObject); if (ResultsCountryMap.Count > 0) ResultsCountryMap[0].MayBeYouWantClickMe(ResultsCountryMap, eventData); }
      
      





不透明なパターンの上に100個以上の透明なパターンがあっても、不透明なパターンが表示されます。



完全なスクリプトコードを提供します。
 using UnityEngine; using System.Collections; using System.Collections.Generic; using UnityEngine.UI; using UnityEngine.EventSystems; using System.Linq; public class CountryMap : MonoBehaviour,IPointerClickHandler { Image CountryImg; Image SelectCountry; public event CountryMapEvent TapEvent; void Awake() { CountryImg = GetComponent<Image>(); SelectCountry = transform.GetChild(0).GetComponent<Image>(); SelectCountry.sprite = Resources.Load<Sprite>("Image/Countries/" + CountryImg.sprite.name); } private bool IsAlphaPoint(PointerEventData eventData) { Vector2 localCursor; RectTransformUtility.ScreenPointToLocalPointInRectangle(GetComponent<RectTransform>(), eventData.position, eventData.pressEventCamera, out localCursor); Rect r = RectTransformUtility.PixelAdjustRect(GetComponent<RectTransform>(), GetComponent<Canvas>()); Vector2 ll = new Vector2(localCursor.x - rx, localCursor.y - ry); int x = (int)(ll.x / r.height * CountryImg.sprite.textureRect.height); int y = (int)(ll.y / r.height * CountryImg.sprite.textureRect.height); if (CountryImg.sprite.texture.GetPixel(x, y).a > 0) return false; else return true; } public void MayBeYouWantClickMe(List<CountryMap> ResultsCountryMap, PointerEventData eventData) { if (!IsAlphaPoint(eventData)) { print(gameObject.name); if (TapEvent != null) TapEvent(this); } else { ResultsCountryMap.Remove(this); if (ResultsCountryMap.Count > 0) ResultsCountryMap[0].MayBeYouWantClickMe(ResultsCountryMap, eventData); } } public void OnPointerClick(PointerEventData eventData) { if(!IsAlphaPoint(eventData)) { print(gameObject.name); if (TapEvent != null) TapEvent(this); } else { List<RaycastResult> raycastResults=new List<RaycastResult>(); EventSystem.current.RaycastAll(eventData, raycastResults); List<CountryMap> ResultsCountryMap = raycastResults.Select(x => x.gameObject.GetComponent<CountryMap>()).ToList(); ResultsCountryMap.RemoveAll(x => x == null || x.gameObject==gameObject); if (ResultsCountryMap.Count > 0) ResultsCountryMap[0].MayBeYouWantClickMe(ResultsCountryMap, eventData); } } public void StopSelect() { StopAllCoroutines(); SelectCountry.color = new Color32(255, 255, 255, 0); } public void StartSelect() { StartCoroutine(Selecting()); } IEnumerator Selecting() { int alpha=0; int count = 0; while (true) { alpha = (int)Mathf.PingPong(count, 150); count = count > 300 ? 0 : count + 5; SelectCountry.color = new Color32(255, 255, 255, (byte)alpha); yield return new WaitForFixedUpdate(); } } }
      
      







そして今、ピクセルを表示することで問題を解決することからのボーナス。 スプライトのパラメーターを設定した図を覚えていますか? そのため、読み取り/書き込みが有効になっているチェックマークがあります。これにより、リストボックスにアクセスできます。 書き込みという言葉から明らかなように、読むだけではありません。



ピクセルは自由に変更できます!



例、スプライトを明るくする:



 Texture2D tex = CountryImg.sprite.texture; Texture2D newTex = (Texture2D)GameObject.Instantiate(tex); newTex.SetPixels32(tex.GetPixels32()); for (int i = 0; i < newTex.width; i++) { for (int j = 0; j < newTex.height; j++) { if (newTex.GetPixel(i, j).a != 0f) newTex.SetPixel(i, j, newTex.GetPixel(i, j)*1.5f); } } newTex.Apply(); CountryImg.sprite = Sprite.Create(newTex, CountryImg.sprite.rect, new Vector2(0.5f, 0.5f));
      
      





それは:



画像



結果:



画像



以上です。 この記事が少なくとも誰かに役立つことを本当に願っています。 質問やコメントがある場合は、コメントを書いてください。



All Articles