UnityのScrollViewで地図(画像)をスクロール

# やりたいこと
2DのUI上で、上下左右にスクロール可能な地図を表示し、地図上の特定の箇所がクリックした場合に特定のアクションを起こせるようにする。

# 想定読者
この記事は、Unityの初歩的な入門書などで、見様見真似でプロジェクトを1つ作ったことがある程度の人を対象にしています。
(つまり、この記事で書いたことを試す前の自分自身です。）

# 結果
できました。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Unityの練習として地図を上下左右にスクロールできるUIを作ってみました。 <a href="https://t.co/ErtpAgq2WZ">pic.twitter.com/ErtpAgq2WZ</a></p>&mdash; 渡邉　晋 (@shinwtnb) <a href="https://twitter.com/shinwtnb/status/1272803383548604416?ref_src=twsrc%5Etfw">June 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
できあがってみれば単純なモノだったのですが、紆余曲折しながら作っているなかで、関係するGemeObjectやComponentの理解に役立ちました。

# プロジェクトの構成
ScrollViewのContentの子オブジェクトにとしたImageに地図画像を張り付けることで実現しました。さらにそのImageの子オブジェクトとしてButtonを張り付けます(下図イメージ図参照）。
ScrollViewのContentの子オブジェクトはPanelとし、そのPanelにImageやButtonを配置する方が折り目正しいやり方のように思われますが、問題なく機能しているので、上記のやり方にしました。
![構成イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/234b48e4-98ad-002c-2669-58d780cca6e5.png)



# 手順
## 事前準備
地図として貼り付けたい画像を準備し、Assetに登録しておきます。

## 基本部分の作成
新規プロジェクトを作成したら、Hierarchyを右クリックして、UI>ScrollViewを選択します。するとCanvasが作られ、その子オブジェクトにScrollViewが作られます。同時にMain Cameraと同階層にEventSystemが作られます。EventSystemはマウス、キーボード入力を受け付けるために必要なので、そのままにしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/450c7f4f-a912-1c6e-12df-7ff6a1a63195.png)


この状態だと、MainCameraの範囲に対してCanvasがバカでかくて作業しにくいので、CanvasのInspectorのCanvasコンポーネントにあるReder Modeを"Screen Space - Overlay"から"Screen Space - Camera"に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/4774f251-fa4b-d050-f5da-73cf047f26df.png)
すると"Render Camera"というフィールドが現れるので、そこにHierarcyツリーからMain Cameraをドロップします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/5daeec5f-387f-97f1-881f-b53c8db9d9bf.png)

Scroll View、さらにその下のViewportを展開し、Contentを表示します。Contentを右クリックして、UI>Imageを選択し、Contentの子オブジェクトにImageを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/95f7e725-12a3-239d-ba13-1c3eb8831343.png)

Imageを作成したら、それを選択し、InspectorのImageコンポーネントにあるSource Imageに、事前準備でAssetに用意しておいた地図画像を登録します。
続いて、同じImageコンポーネントにある"Set Native Size"をクリックして、Imageオブジェクトのサイズを地図画像のサイズに合わせます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/87b468b8-f400-1665-a18d-742971a9815c.png)
Assetに登録した画像ファイルを直接Hierarcyにドラッグ&ドロップしても良さそうに思えますが、その場合、画像ファイルは2D ObjectのSpriteとして追加されてしまい、ScrolViewの範囲外の部分も表示されてしまいます。

ContentのサイズもImage(及び地図画像)のサイズに合わせます。マニュアルで合わせてもいいですが、自動調整させた方が何かと便利です。それには、Contentsに"Layout Element"、"Content Size Fitter"コンポーネントを追加し、"Layout Element"の"Min Width"、"Min Height"にImageの幅と高さを入力し、"Content Size Fitter"の"Horizontal Fit"と"Vertical Fit"をいずれも"Preferred Size"にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/27edc841-edab-03cd-6960-34efcf6ae4e9.png)

ScrollViewのサイズを所望のサイズに調整します。ここでは、Canvasの左3/4くらいのサイズにしました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/c86dea17-9fb6-2ca0-e2fc-84aca46dac04.png)



最後にButtonを配置します。(下図ではスクリプトを追加済みのbuttonをPrefab化して追加しています。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/e41c0d53-507d-c2a9-35a5-42ad1c7c3ced.png)
Buttonのアクションとしては、左上に追加したInputFieldにクリックしたボタン名が入力されるようにしました。ボタン名を都市と同じにすれば都市名が入力されるという算段です。Buttonのスクリプトは以下のとおりです。

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class CityButton : MonoBehaviour
{
    public InputField input_field;
    // Start is called before the first frame update
    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    
    public void OnClick()
    {
        input_field.text = name;
    }

}
```
このスクリプト名は"CityButton"としています。これをButtonに追加すると、ButtonのInspectorに"City Button(Script)"というコンポーネントが現れます。そのにPublic変数で定義した"Input_field"が出てきますので、Hierarcyから値を入力したいInput Fieldをドラッグ＆ドロップして登録します。(複数の都市のボタンが同じInputFieldに値を入力するので、本来ならば```void Start()```関数のなかで```GameObject.Find()```を使って取得すべきでしょうが、簡略化しました。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230102/52c5c54b-5fba-2bdd-5c7d-8f47b06df138.png)

## 微調整
上記の通り作った場合、初期状態では地図の左上が表示されます。これを地図の真ん中にするためには、ContentのRect TransformのAnchorsのPivotのXとYを両方とも0.5にします。
または、スクリプトで、ScrollViewのScroll RectコンポーネントのverticalNormalizedPosition, horizontalNormalizedPositonに値を設定してもOkです。具体的には、以下のスクリプトをScrollViewに追加してやります。

```
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class PictureScroll : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        var scRect = this.GetComponent<ScrollRect>();
        scRect.verticalNormalizedPosition = 0.5f;
        scRect.horizontalNormalizedPosition = 0.5f;
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}
```


水平、垂直のスクロールバーを表示したくないときは、Scrollbar HorizontalのRect TransformのHeightを0、Scrollbar VerticalのRect TransformのWidthを0にします。

また、元の地図に上下左右のマージンを追加したいときは、Contentに追加したLayout ElementのPreferred Width, Preferred Heightをマージンを加えた値にします。

# 今後の拡張
地図のズームにも対応したいです。