Unityでドラッグ&ドロップして、オブジェクトのマテリアルを変更する方法

# はじめに
こんにちは、たこです🐙
この記事は[Life is Tech ! Tokai Advent Calendar](https://qiita.com/advent-calendar/2022/life-is-tech-tokai) 2022の16日目の記事です。

今回は、Unityで、Canvasにある画像リストからドラッグ&ドロップすると、オブジェクトのマテリアルをその画像に変更する実装について紹介しようと思います！

# 完成イメージ🎄
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/4d9ac0a8-9aa1-b285-f41d-e33ea5d54dd8.gif" width="80%">

# 準備
#### ①Cubeを配置する
Hierarchyウィンドウで、3D Objects>Cubeを選択して、Cubeを配置します。

#### ②素材を用意する
オブジェクトのマテリアルにしたい画像を複数枚用意して、インポートします。
インポートしたら、TextureTypeを __Sprite(2D and UI)__ に変更します。

私は、こんな感じで、クリスマスの画像にしました〜🎁
![スクリーンショット 2022-12-16 17.23.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/e8178d62-e456-5e97-58ed-7bd3177663cd.png)


#### ③Materialを追加する
用意した素材の分だけ、Materialを作成します。
MainMapの __Albedo左の四角__ に、素材の画像をドラッグ&ドロップします。
すると、画像をオブジェクトに貼り付けられるようになります。
![スクリーンショット 2022-12-16 17.48.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/2273eb15-911d-fa34-2742-fba0070c96cc.png)

![スクリーンショット 2022-12-16 17.26.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/8822e0e0-6792-df2a-8715-7012e05cab1c.png)

#### ④画像リストをGameビューに表示する
Hierarchyウィンドウで、Canvas>Scroll Viewを選択し、Scroll Viewを追加します。

Scroll View>Viewport>Contentの中に、Imageを追加します。
作成したImageのImageコンポーネントの __SorceImage__ に、準備で作った素材をドラッグ&ドロップします。
用意した素材の分だけ、この作業を繰り返します。
![スクリーンショット 2022-12-16 18.02.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/6fb1cff2-0a39-be77-38c7-040eec6a7563.png)

Scroll Viewの大きさを調整したり、Contentに  __HorizontalLayoutGroup__ コンポーネントを追加したりすると、作成したリストを綺麗に整えることができます。
![スクリーンショット 2022-12-16 17.58.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/aaa4cb57-947a-b5e3-abec-4cb9063e0885.png)


# ドラッグ&ドロップを実装
ここからは、いよいよドラッグ&ドロップの実装に入ります。
まずは、ドラッグするオブジェクトにつけるスクリプトを書きます。
ProjectウィンドウでC#Scriptを作成します。

#### 1.ドラッグし始め
```c#
// ドラッグし始め
    public void OnBeginDrag(PointerEventData pointerEventData)
    {
        CreateDragObject();

        draggingObject.transform.position = pointerEventData.position;
    }


// ドラッグ中にカーソルについてくるオブジェクトを作成
    void CreateDragObject()
    {
        draggingObject = new GameObject("Dragging Object");

        draggingObject.transform.SetParent(canvasTran);
        draggingObject.transform.SetAsLastSibling();
        draggingObject.transform.localScale = Vector3.one;


        // レイキャストがブロックされないように
        CanvasGroup canvasGroup = draggingObject.AddComponent<CanvasGroup>();
        canvasGroup.blocksRaycasts = false;

        // ドラッグ中にカーソルについてくるオブジェクトの画像をドラッグした画像にする
        Image draggingImage = draggingObject.AddComponent<Image>();
        Image sourceImage = GetComponent<Image>();

        draggingImage.sprite = sourceImage.sprite;
        draggingImage.rectTransform.sizeDelta = sourceImage.rectTransform.sizeDelta;
        draggingImage.color = sourceImage.color;
        draggingImage.material = sourceImage.material;


        //　ドラッグした画像の色をグレーにする
        this.GetComponent<Image>().color = Vector4.one * 0.6f;
    }
```

#### 2.ドラッグ中
```c#
// ドラッグ中
    public void OnDrag(PointerEventData pointerEventData)
    {

        draggingObject.transform.position = pointerEventData.position;

        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit,20))
        {
            draggingObject.GetComponent<Image>().color = new Color(0.8f, 0.8f, 0.8f, 1);
        }
        else
        {
            draggingObject.GetComponent<Image>().color = new Color(1, 1, 1, 0.2f);
        }
    }
```
#### 3.ドロップ後
```c#
 // ドラッグ終わり
    public void OnEndDrag(PointerEventData pointerEventData)
    {
       
        this.GetComponent<Image>().color = defaultColor;
        Destroy(draggingObject);

        if (hit.point == new Vector3(0, 0, 0))
        {
            return;
        }
        else
        {
            if (hit.collider.gameObject != null)
            {
                // ここにドロップした先のオブジェクトのマテリアルを変える命令を書く
            }

        } 
    }
```

#### 完成形
```c#
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class Draggable : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler
{
    Transform canvasTran;
    GameObject draggingObject;

    Color defaultColor;

    Ray ray;
    RaycastHit hit;

    [SerializeField] int materialNumber;

    void Awake()
    {
        canvasTran = transform.root;
        defaultColor = gameObject.GetComponent<Image>().color;
    }

    // ドラッグし始め
    public void OnBeginDrag(PointerEventData pointerEventData)
    {
        CreateDragObject();

        draggingObject.transform.position = pointerEventData.position;
    }

    // ドラッグ中
    public void OnDrag(PointerEventData pointerEventData)
    {

        draggingObject.transform.position = pointerEventData.position;

        ray = Camera.main.ScreenPointToRay(Input.mousePosition);
        if (Physics.Raycast(ray, out hit,20))
        {
            draggingObject.GetComponent<Image>().color = new Color(0.8f, 0.8f, 0.8f, 1);
        }
        else
        {
            draggingObject.GetComponent<Image>().color = new Color(1, 1, 1, 0.2f);
        }
    }

    // ドラッグ終わり
    public void OnEndDrag(PointerEventData pointerEventData)
    {
       
        this.GetComponent<Image>().color = defaultColor;
        Destroy(draggingObject);

        if (hit.point == new Vector3(0, 0, 0))
        {
            return;
        }
        else
        {
            if (hit.collider.gameObject != null)
            {
               // ここにドロップした先のオブジェクトのマテリアルを変える命令を書く
            }

        } 
    }

    // ドラッグ中にカーソルについてくるオブジェクトを作成
    void CreateDragObject()
    {
        draggingObject = new GameObject("Dragging Object");

        draggingObject.transform.SetParent(canvasTran);
        draggingObject.transform.SetAsLastSibling();
        draggingObject.transform.localScale = Vector3.one;


        // レイキャストがブロックされないように
        CanvasGroup canvasGroup = draggingObject.AddComponent<CanvasGroup>();
        canvasGroup.blocksRaycasts = false;

        // ドラッグ中にカーソルについてくるオブジェクトの画像をドラッグした画像にする
        Image draggingImage = draggingObject.AddComponent<Image>();
        Image sourceImage = GetComponent<Image>();

        draggingImage.sprite = sourceImage.sprite;
        draggingImage.rectTransform.sizeDelta = sourceImage.rectTransform.sizeDelta;
        draggingImage.color = sourceImage.color;
        draggingImage.material = sourceImage.material;


        //　ドラッグした画像の色をグレーにする
        this.GetComponent<Image>().color = Vector4.one * 0.6f;
    }

}

```

#### スクリプトをアタッチ
作成したスクリプトをScroll View>Viewport>Contentの中に入っている __Imageそれぞれ__ に __アタッチ__ します。


# オブジェクトのマテリアルを変更する
最後に、ドロップしたらCubeのマテリアルをドロップした画像に変更できるようにします。

まず、Projectウィンドウで新しくスクリプトを作成します。
作成したスクリプトは、 __Cube__ にアタッチします。
```c#
using UnityEngine;

public class MaterialChanger : MonoBehaviour
{
    [SerializeField] Material[] materials;

    // ドロップされたら呼ばれる関数
    public void ChangeMaterial(int materialNum) 
    {
        this.GetComponent<MeshRenderer>().material = materials[materialNum];
    }
}
```
そして、Materialの配列に、準備で作ったMaterialをアタッチしていきます。
![スクリーンショット 2022-12-16 21.11.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/f916ed78-ab26-4245-7bd9-e12ab99281d5.png)


次に、先ほど作成した **ドラッグ&ドロップのスクリプト** を編集します。
```c#
// ドラッグ終わり
    public void OnEndDrag(PointerEventData pointerEventData)
    {
       
        this.GetComponent<Image>().color = defaultColor;
        Destroy(draggingObject);

        if (hit.point == new Vector3(0, 0, 0))
        {
            return;
        }
        else
        {
            if (hit.collider.gameObject != null)
            {
                // ここを追加
                // Cubeのマテリアルを変更する関数を呼び出し
                // 引数に画像リストの番号を渡す
                hit.collider.gameObject.GetComponent<MaterialChanger>().ChangeMaterial(materialNumber);
            }

        } 
    }
```

追加をしたら、UnityのInspectorウィンドウで、画像リストのそれぞれの画像に番号を指定します。
**注意　CubeのMaterialの配列と同じ順番になるように振ってくだい。**
![スクリーンショット 2022-12-16 21.14.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/728526/bd2113a4-d52c-3368-ce09-79d7857a8ae8.png)


ここまでできたら、完成です！


# まとめ
以上、Unityで、ドラッグ&ドロップしてオブジェクトのマテリアルを好きな画像に変更する方法でした〜🎅🎄✨